- Как бороться с ошибкой «java.lang.OutOfMemoryError: Java heap space»?
- 18 ответов
- Какие бывают типы OutOfMemoryError или из каких частей состоит память java процесса
- 1. java.lang.OutOfMemoryError: Java heap space
- 2. java.lang.OutOfMemoryError: PermGen space
- 3. java.lang.OutOfMemoryError: GC overhead limit exceeded
- 4. java.lang.OutOfMemoryError: unable to create new native thread
Как бороться с ошибкой «java.lang.OutOfMemoryError: Java heap space»?
Я пишу клиентское приложение Swing (графический дизайнер шрифтов) на Java 5. В последнее время я столкнулся с ошибкой java.lang.OutOfMemoryError: Java heap space , потому что я не консервативен в использовании памяти. Пользователь может открывать неограниченное количество файлов, а программа сохраняет открытые объекты в памяти. После быстрого исследования я нашел эргономику в виртуальной машине 5.0 Java, а другие, говорящие на машине Windows, имеют максимальный размер кучи JVM как 64MB . Учитывая эту ситуацию, как мне решить эту проблему? Я могу увеличить максимальный размер кучи с помощью параметра командной строки для java, но для этого потребуется выяснить доступную оперативную память и написать какую-то программу запуска или script. Кроме того, увеличение до некоторого конечного максимума в конечном итоге не устраняет проблему. Я мог бы переписать часть моего кода, чтобы часто сохранять объекты в файловой системе (с использованием базы данных — одно и то же), чтобы освободить память. Это может сработать, но это, вероятно, тоже много работает. Если бы вы могли указать мне на подробности вышеизложенных идей или на некоторые альтернативы, такие как автоматическая виртуальная память, динамически расширяющая размер кучи, это будет здорово.
Максимальный размер кучи по умолчанию составляет 64 МБ, начиная с версии J2SE 5.0. Информацию о J2SE 8.0 см. В разделе «Эргономика сборщика мусора» по адресу docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Если вы попали сюда из-за того, что каждый вопрос OOM дублирован на этот вопрос, обязательно проверьте также: stackoverflow.com/questions/299659/… Он предоставляет решение для очистки ссылок на память «как раз вовремя» перед OOM. SoftReferences может быть инструментом, который решает вашу актуальную проблему.
18 ответов
В конечном итоге у вас всегда есть конечная максимальная куча для использования независимо от того, на какой платформе вы работаете. В Windows 32 бит это около 2 ГБ (не в кучу, а общий объем памяти на процесс). Просто случается, что Java хочет уменьшить размер по умолчанию (предположительно, чтобы программист не мог создавать программы, у которых есть выделенное выделение памяти, не сталкиваясь с этой проблемой, и нужно точно изучить, что они делают).
Итак, это дает несколько подходов, которые вы могли бы предпринять, чтобы определить, какой объем памяти вам нужен или уменьшить объем используемой памяти. Одна распространенная ошибка с сборками мусора, такими как Java или С#, заключается в том, чтобы поддерживать ссылки на объекты, которые вы больше не используете, или выделять многие объекты, когда вы можете их повторно использовать. Пока объекты ссылаются на них, они будут продолжать использовать кучу пространства, так как сборщик мусора не удалит их.
В этом случае вы можете использовать профилировщик памяти Java, чтобы определить, какие методы в вашей программе распределяют большое количество объектов, а затем определить, есть ли способ убедиться, что они больше не ссылаются или не выделять их в первое место. Одним из вариантов, который я использовал в прошлом, является «JMP» http://www.khelekore.org/jmp/.
Если вы определяете, что вы распределяете эти объекты по какой-либо причине, и вам нужно поддерживать ссылки (в зависимости от того, что вы делаете, это может быть так), вам просто нужно увеличить максимальный размер кучи при запуске программа. Однако, как только вы выполняете профилирование памяти и понимаете, как ваши объекты распределяются, вы должны иметь лучшее представление о том, сколько памяти вам нужно.
В общем случае, если вы не можете гарантировать, что ваша программа будет работать в некотором конечном объеме памяти (возможно, в зависимости от размера ввода), вы всегда столкнетесь с этой проблемой. Только после исчерпания всего этого вам нужно будет искать объекты кэширования на диске и т.д. На этом этапе у вас должна быть очень веская причина сказать «Мне нужно Xgb памяти» для чего-то, и вы не можете обойти его, улучшив ваши алгоритмы или шаблоны распределения памяти. Обычно это обычно будет иметь место для алгоритмов, работающих на больших наборах данных (например, в базе данных или в какой-либо программе научного анализа), а затем становятся полезными такие методы, как кэширование и отображение IO с памятью.
Какие бывают типы OutOfMemoryError или из каких частей состоит память java процесса
Если вы словили OutOfMemoryError, то это вовсе не значит, что ваше приложение создает много объектов, которые не могут почиститься сборщиком мусора и заполняют всю память, выделенную вами с помощью параметра -Xmx. Я, как минимум, могу придумать два других случая, когда вы можете увидеть эту ошибку. Дело в том, что память java процесса не ограничивается областью -Xmx, где ваше приложение программно создает объекты.
Область памяти, занимаемая java процессом, состоит из нескольких частей. Тип OutOfMemoryError зависит от того, в какой из них не хватило места.
1. java.lang.OutOfMemoryError: Java heap space
Не хватает место в куче, а именно, в области памяти в которую помещаются объекты, создаваемые программно в вашем приложении. Размер задается параметрами -Xms и -Xmx. Если вы пытаетесь создать объект, а места в куче не осталось, то получаете эту ошибку. Обычно проблема кроется в утечке памяти, коих бывает великое множество, и интернет просто пестрит статьями на эту тему.
2. java.lang.OutOfMemoryError: PermGen space
Данная ошибка возникает при нехватке места в Permanent области, размер которой задается параметрами -XX:PermSize и -XX:MaxPermSize. Что там лежит и как бороться с OutOfMemoryError возникающей там, я уже описал подробнейшим образом тут.
3. java.lang.OutOfMemoryError: GC overhead limit exceeded
Данная ошибка может возникнуть как при переполнении первой, так и второй областей. Связана она с тем, что памяти осталось мало и GC постоянно работает, пытаясь высвободить немного места. Данную ошибку можно отключить с помощью параметра -XX:-UseGCOverheadLimit, но, конечно же, её надо не отключать, а либо решать проблему утечки памяти, либо выделять больше объема, либо менять настройки GC.
4. java.lang.OutOfMemoryError: unable to create new native thread
Впервые я столкнулся с данной ошибкой несколько лет назад, когда занимался нагрузочным тестированием и пытался выяснить максимальное количество пользователей, которые могут работать с нашим веб-приложением. Я использовал специальную тулзу, которая позволяла логинить пользователей и эмулировать их стандартные действия. На определенном количестве клиентов, я начал получать OutOfMemoryError. Не особо вчитываясь в текст сообщения и думая, что мне не хватает памяти на создание сессии пользователя и других необходимых объектов, я увеличил размер кучи приложения (-Xmx). Каково же было мое удивление, когда после этого количество пользователей одновременно работающих с системой только уменьшилось. Давайте подробно разберемся как же такое получилось.
На самом деле это очень просто воспроизвести на windows на 32-битной машине, так как там процессу выделяется не больше 2Гб.
Допустим у вас есть приложение с большим количеством одновременно работающих пользователей, которое запускается с параметрами -Xmx1024M -XX:MaxPermSize=256M -Xss512K. Если всего процессу доступно 2G, то остается свободным еще коло 768M. Именно в данном остатке памяти и создаются стеки потоков. Таким образом, примерно вы можете создать не больше 768*(1024/512)=1536 (у меня при таких параметрах получилось создать 1316) нитей (см. рисунок в начале статьи), после чего вы получите OutOfMemoryError. Если вы увеличиваете -Xmx, то количество потоков, которые вы можете создать соответственно уменьшается. Вариант с уменьшением -Xss, для возможности создания большего количества потоков, не всегда выход, так как, возможно, у вас существуют в системе потоки требующие довольно больших стеков. Например, поток инициализации или какие-нибудь фоновые задачи. Но все же выход есть. Оказывается при программном создании потока, можно указать размер стека: Thread(ThreadGroup group, Runnable target, String name,long stackSize). Таким образом вы можете выставить -Xss довольно маленьким, а действия требующие больших стеков, выполнять в отдельных потоках, созданных с помощью упомянутого выше конструктора.
Более подробно, что же лежит в стеке потока, и куда уходит эта память, можно прочитать тут.
Конечно, вам может показаться данная проблема слегка надуманной, так как большинство серверов нынче крутиться на 64-битной архитектуре, но все же считаю данный пример весьма полезным, так как он помогает разобраться из каких частей состоит память java-процесса.