Страницы

Поиск по вопросам

четверг, 2 января 2020 г.

Память JVM: как оптимизировать многопоточное приложение?

#java #многопоточность #memory


Господа,
насколько я понимаю модель памяти JVM выглядит так: если поток вызывает некий метод
класса, то все локальные переменные которые там используются будут созданы не в HeapMemory,
а в StackMemory. Этот StackMemory тоже очищается GC и тоже входит как слагаемое в полную
доступную JVM память (ее можно получить как Runtime.maxMemory()), и, следовательно,
чем больше потоков - тем больше занято памяти, даже при том что HeapMemory остается
без изменений. Т.о. можно записать:
Runtime.freeMemory() = Runtime.maxMemory() - Thread1Stack - Thread2Stack - ... -
HeapSpace.
Поправьте меня, пожалуйста, если я не прав.
К чему я это спрашиваю: в моем приложении есть специальный поток-наблюдатель, который
все время логгирует значение Runtime.freeMemory() и иногда оно опускается до сотен
байт (однажды вообще было Runtime.freeMemory() = 8 ). Это приводит к тому, что процесс
замедляется и Томкат не отвечает на HTTP запросы. Возникает вопрос: может ли при таком
низком значении памяти запускаться GC?
Я предполагаю, что должна быть связь оптимального количества потоков не только с
количеством процессоров, но и с размером памяти. Как мне определить эту связь?
У меня Томкату доступно Runtime.maxMemory() = 64МБ, при этом обычно мое приложение
запускает 3-4 потока, максимум до 10-12. Мне казалось, что такая нагрузка подходит
для процессора т.к. все эти мои потоки очень много спят. Однако судя по тому как мало
памяти остается - думать надо не только о процессоре, но и о памяти. 
Итак, как понять какое количество потоков оптимально для многопоточного приложения?
Какие кроме Runtime.freeMemory() методы отслеживания состояния памяти вы используете?
(я не могу запустить профайлер на удаленном хостинге).    


Ответы

Ответ 1



Вы не правы. Объекты всегда создаются в куче. В стеке живут примитивные типы (byte, char, short, int, long, boolean). В стеке хранятся ссылки на объекты в куче. Все ваши последующие рассуждения, как следствие, не верны. Если у вас всё встревает или даже падает по OOM, то у вас утечка. В общем, количетсво потоков и память связаны только когда потоков очень много (тысячи). Кроме того, каждому потоку можно указать конкретный размер стека, если вы знаете, какова максимальная требуемая глубина и нет рекурсии. Так что количетсво потоков выбирается таким образом, чтобы оно по возможности не было пропорционально количеству данных приложения и количеству пользователей. Что же до трёх потоков, то вы забываете о самом томкате, который запускает... очень немало потоков (в зависимости от конфигурации).

Ответ 2



"Этот StackMemory тоже очищается GC" - неверно. Размер стека постоянен. Заполненая часть меняется, но не с помощью GC, а при процедурных переходах. Другое дело, что GC туда заглядывает, чтобы найти ссылки на живые объекты, которые не надо вычищать.

Комментариев нет:

Отправить комментарий