Как бороться с ошибкой «java.lang.OutOfMemoryError: Java heap space»?

416

Я пишу клиентское приложение Swing (графический дизайнер шрифтов) на Java 5 . В последнее время я сталкиваюсь с java.lang.OutOfMemoryError: Java heap spaceошибкой, потому что я не консервативен в использовании памяти. Пользователь может открывать неограниченное количество файлов, а программа сохраняет открытые объекты в памяти. После быстрого исследования я обнаружил, что эргономика в виртуальной машине Java 5.0 и другие говорят, что на машине с Windows JVM по умолчанию имеет максимальный размер кучи, равный 64MB.

Учитывая эту ситуацию, как мне справиться с этим ограничением?

Я мог бы увеличить максимальный размер кучи, используя опцию командной строки для java, но это потребовало бы выяснения доступной оперативной памяти и написания некоторой запускающей программы или скрипта. Кроме того, увеличение до некоторого конечного максимума в конечном итоге не избавит от проблемы.

Я мог бы переписать часть своего кода, чтобы часто сохранять объекты в файловой системе (использование базы данных - то же самое), чтобы освободить память. Это может сработать, но, вероятно, это тоже много работы.

Если бы вы могли указать мне на детали вышеупомянутых идей или некоторые альтернативы, такие как автоматическая виртуальная память, динамически увеличивая размер кучи , это было бы здорово.

Евгений Йокота
источник
Максимальный размер кучи по умолчанию составляет 64 МБ, начиная с версии J2SE 5.0. Информацию о J2SE 8.0 см. В разделе «Эргономика сборщика мусора» по адресу docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Энди Томас
Если вы попали сюда из-за того, что каждый вопрос OOM дублирован на этот вопрос, обязательно проверьте также: stackoverflow.com/questions/299659/… Он предоставляет решение для очистки ссылок на память «как раз вовремя» перед OOM. SoftReferences может быть инструментом, который решает вашу актуальную проблему.
Стив Штайнер,

Ответы:

244

В конечном итоге у вас всегда есть конечный максимум кучи для использования независимо от того, на какой платформе вы работаете. В Windows 32 бит это примерно 2GB(не куча, а общий объем памяти на процесс). Просто так получается, что Java решает уменьшить размер по умолчанию (предположительно, так, чтобы программист не мог создавать программы с распределенным выделением памяти, не сталкиваясь с этой проблемой и не проверяя, что именно они делают).

Таким образом, учитывая, что есть несколько подходов, которые вы можете использовать, чтобы определить, какой объем памяти вам нужен, или уменьшить объем используемой памяти. Одной из распространенных ошибок в языках сборки мусора, таких как Java или C #, является сохранение ссылок на объекты, которые вы больше не используете, или выделение множества объектов, когда вы можете использовать их повторно . Пока объекты имеют ссылку на них, они будут продолжать использовать пространство кучи, поскольку сборщик мусора не удалит их.

В этом случае вы можете использовать профилировщик памяти Java, чтобы определить, какие методы в вашей программе выделяют большое количество объектов, а затем определить, есть ли способ убедиться, что на них больше нет ссылок, или вообще не выделять их. Одним из вариантов, который я использовал в прошлом, является «JMP» http://www.khelekore.org/jmp/ .

Если вы решите, что вы распределяете эти объекты по какой-то причине и вам нужно хранить ссылки (в зависимости от того, что вы делаете, это может иметь место), вам просто нужно будет увеличить максимальный размер кучи при запуске программы. Однако, как только вы выполните профилирование памяти и поймете, как распределяются ваши объекты, у вас должно быть лучшее представление о том, сколько памяти вам нужно.

В общем, если вы не можете гарантировать, что ваша программа будет работать в ограниченном объеме памяти (возможно, в зависимости от размера ввода), вы всегда столкнетесь с этой проблемой. Только после исчерпания всего этого вам нужно будет изучить кэширование объектов на диск и т. Д. В этот момент у вас должна быть очень веская причина сказать «мне нужен Xgb памяти» для чего-то, и вы не сможете обойти это, улучшив ваши алгоритмы или шаблоны распределения памяти. Обычно это будет иметь место только в случае алгоритмов, работающих с большими наборами данных (например, с базой данных или какой-либо программой научного анализа), и тогда становятся полезными такие методы, как кэширование и ввод-вывод в память.

Бен Чайлдс
источник
6
OpenJDK и OracleJDK имеют встроенный профилировщик - jvisualvm. Если вы хотите больше удобств, я бы предложил коммерческий Yourkit.
Петр Гладких
121

Запустите Java с параметром командной строки -Xmx, который устанавливает максимальный размер кучи.

Смотрите здесь для деталей .

Дэйв Уэбб
источник
3
Как установить этот параметр навсегда? Потому что я использую команду "Gradlew Assembly".
Dr.jacky
2
Выполнить-> Выполнить конфигурации-> Щелкните по аргументам-> внутри аргументов VM введите -Xms1g -Xmx2g
Араян Сингх
2
Это настоящий ответ.
NCCC
85

Вы можете указать для каждого проекта, сколько пространства кучи хочет ваш проект

Следующее для Eclipse Helios / Juno / Kepler :

Клик правой кнопкой мыши

 Run As - Run Configuration - Arguments - Vm Arguments, 

затем добавьте это

-Xmx2048m
allenhwkim
источник
1
Привет bighostkim и cuongHuyTo, где "Аргументы" .. Я могу видеть до запуска конфигурации. Пожалуйста, скажи мне. Мне нужно скачать и сохранить почти 2000 контактов из Gmail. Это происходит сбой из-за
нехватки
@AndroiRaji: вы щелкаете правой кнопкой мыши по классу Java, который имеет работоспособный main (то есть «public static void main (String [] args)»), затем выбираете Run As - Run Configuration. Затем «Аргументы» - это вкладка сразу после основной (вы видите вкладки Главная, Аргументы, JRE, Путь к классам, Источник, Среда, Общие).
CuongHuyTo
47

Увеличение размера кучи - это не «исправление», это «штукатурка», 100% временная. Это снова рухнет в другом месте. Чтобы избежать этих проблем, напишите высокопроизводительный код.

  1. Используйте локальные переменные везде, где это возможно.
  2. Убедитесь, что вы выбрали правильный объект (например: Выбор между String, StringBuffer и StringBuilder)
  3. Используйте хорошую систему кодирования для вашей программы (пример: использование статических переменных против статических переменных)
  4. Другие вещи, которые могут работать с вашим кодом.
  5. Попробуй двигаться с МНОГО РАЗРЕЗЫМИ
Лимонный сок
источник
Это так верно. Я пытаюсь решить одну проблему, когда я получаю OOM в потоке AWT, но если я использую другой новый поток, я не получаю проблему OOM. Все, что я могу найти в Интернете, это увеличить размер кучи для потока AWT.
Ашиш
@Ash: Да, исправьте проблему с ядром вместо того, чтобы искать пластыри.
Лимонный сок
Сборка мусора и подход к управлению памятью в Java должны были решить все эти сложности, связанные с malloc-dealloc его предшественников :( Конечно, я полностью согласен с этим ответом, просто жаль, что значения по умолчанию не облегчают написание кода с обедненными данными -структуры, которые были убраны как можно скорее.
Давос,
31

Большое предостережение ---- в моем офисе мы обнаружили, что (на некоторых компьютерах с Windows) мы не можем выделить более 512 м для кучи Java. Это связано с тем, что на некоторых из этих компьютеров установлен антивирус Касперского. После удаления этого продукта AV мы обнаружили, что можем выделить как минимум 1,6 ГБ, т. Е. -Xmx1600m (M обязательно, иначе это приведет к другой ошибке «Слишком маленькая начальная куча»).

Не знаю, происходит ли это с другими AV-продуктами, но, вероятно, это происходит потому, что AV-программа резервирует небольшой блок памяти в каждом адресном пространстве, тем самым предотвращая одно действительно большое выделение.

Дэвид
источник
22

Аргументы VM работали для меня в затмении. Если вы используете eclipse версии 3.4, сделайте следующее

перейдите Run --> Run Configurations -->затем выберите проект в maven build ->, затем выберите вкладку «JRE» -> затем введите -Xmx1024m.

В качестве альтернативы вы можете сделать, Run --> Run Configurations --> select the "JRE" tab -->затем введите -Xmx1024m

Это должно увеличить кучу памяти для всех сборок / проектов. Приведенный выше объем памяти составляет 1 ГБ. Вы можете оптимизировать, как вы хотите.

люблю все
источник
18

Да, -Xmxвы можете настроить больше памяти для вашей JVM. Чтобы быть уверенным, что вы не утечки или тратить память. Возьмите дамп кучи и используйте Eclipse Memory Analyzer для анализа вашего потребления памяти.

kohlerm
источник
JVMJ9VM007E Параметр командной строки не распознан: -Xmx Не удалось создать виртуальную машину Java. Downvote
Филипп Рего
17

Я хотел бы добавить рекомендации по устранению неисправностей оракула статьи о .

Исключение в потоке имя_потока: java.lang.OutOfMemoryError: пространство кучи Java

Подробное сообщение пространство кучи Java указывает, что объект не может быть выделен в куче Java. Эта ошибка не обязательно означает утечку памяти

Возможные причины:

  1. Простая проблема конфигурации , когда указанный размер кучи недостаточен для приложения.

  2. Приложение непреднамеренно хранит ссылки на объекты , что предотвращает сбор мусора.

  3. Чрезмерное использование финализаторов .

Еще один потенциальный источник этой ошибки возникает в приложениях, которые чрезмерно используют финализаторы. Если у класса есть метод finalize, то объекты этого типа не освобождают свое пространство во время сборки мусора.

После сборки мусора объекты ставятся в очередь для завершения , что происходит позже. финализаторы выполняются потоком демона, который обслуживает очередь финализации. Если поток финализатора не может идти в ногу с очередью финализации, тогда куча Java может заполниться, и этот тип исключения OutOfMemoryError будет выдан .

Один из сценариев, который может вызвать эту ситуацию, заключается в том, что приложение создает высокоприоритетные потоки, которые приводят к увеличению очереди завершения со скоростью, превышающей скорость, с которой поток финализатора обслуживает эту очередь.

Равиндра Бабу
источник
9

Выполните следующие шаги:

  1. Открыть catalina.shиз кота / бин.

  2. Изменить JAVA_OPTS на

    JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m 
    -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m 
    -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
  3. Перезагрузите свой кот

Прадип Бхатт
источник
8

Я читал где-то еще, что вы можете попробовать - поймать java.lang.OutOfMemoryError и в блоке catch вы можете освободить все ресурсы, которые, как вы знаете, могут использовать много памяти, закрыть соединения и т. Д., Затем выполнить System.gc() затем повторить попытку ты собирался сделать.

Другой способ - это, хотя, я не знаю, будет ли это работать, но в настоящее время я проверяю, будет ли это работать в моем приложении.

Идея состоит в том, чтобы выполнить сборку мусора, вызвав System.gc (), который, как известно, увеличивает свободную память. Вы можете продолжать проверять это после того, как код памяти выполняет.

//Mimimum acceptable free memory you think your app needs
long minRunningMemory = (1024*1024);

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();
Mwangi
источник
6
В общем, я думаю, что JVM предпочтет собирать мусор (GC), а не выбрасывать ошибку OutOfMemoryError. Явный вызов System.gc () после OutOfMemoryError может помочь на некоторых виртуальных машинах / конфигурациях, но я не ожидаю, что он будет работать очень хорошо в общем случае. Однако удаление ненужных ссылок на объекты определенно поможет почти во всех случаях.
Майк Кларк
6
@mwangi Вызов System.gc () непосредственно из кода, как правило, плохая идея. Это всего лишь предложение для JVM, что GC должен быть выполнен, но нет абсолютно никакой гарантии, что он будет выполнен.
7

Простой способ решить проблему OutOfMemoryErrorв Java - увеличить максимальный размер кучи с помощью параметров JVM.-Xmx512M , это немедленно решит вашу ошибку OutOfMemoryError. Это мое предпочтительное решение, когда я получаю OutOfMemoryError в Eclipse, Maven или ANT при создании проекта, потому что в зависимости от размера проекта вы можете легко исчерпать память.

Вот пример увеличения максимального размера кучи JVM. Также лучше сохранить соотношение -Xmx к -Xms либо 1: 1, либо 1: 1,5, если вы устанавливаете размер кучи в своем Java-приложении.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Ссылка Ссылка

chaukssey
источник
1
Есть идеи, почему мы должны держать их в соотношении 1: 1 или 1: 1,5?
Эрнесто
7

По умолчанию для разработки JVM использует небольшой размер и небольшую конфигурацию для других функций, связанных с производительностью. Но для производства вы можете настроить, например: (Кроме того, может существовать специфическая конфигурация сервера приложений) -> (Если памяти все еще недостаточно для удовлетворения запроса, и куча уже достигла максимального размера, возникнет ошибка OutOfMemoryError)

-Xms<size>        set initial Java heap size
-Xmx<size>        set maximum Java heap size
-Xss<size>        set java thread stack size

-XX:ParallelGCThreads=8
-XX:+CMSClassUnloadingEnabled
-XX:InitiatingHeapOccupancyPercent=70
-XX:+UnlockDiagnosticVMOptions
-XX:+UseConcMarkSweepGC
-Xms512m
-Xmx8192m
-XX:MaxPermSize=256m (in java 8 optional)

Например: На платформе Linux для производственного режима предпочтительны настройки.

После загрузки и настройки сервера этим способом http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1. создать файл setenv.sh в папке / opt / tomcat / bin /

   touch /opt/tomcat/bin/setenv.sh

2. Откройте и запишите эти параметры для настройки предпочтительного режима.

nano  /opt/tomcat/bin/setenv.sh 

export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+CMSClassUnloadingEnabled"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=70"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockDiagnosticVMOptions"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseConcMarkSweepGC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=256M"

3.service tomcat restart

Обратите внимание, что JVM использует больше памяти, чем просто кучу. Например, методы Java, стеки потоков и собственные дескрипторы размещаются в памяти отдельно от кучи, а также внутренних структур данных JVM.

Musa
источник
7

Я столкнулся с той же проблемой от размера кучи Java.

У меня есть два решения, если вы используете Java 5 (1.5).

  1. просто установите jdk1.6 и перейдите в настройки eclipse и установите путь jre jav1 1.6, как вы установили.

  2. Проверьте аргумент вашей виртуальной машины и пусть он будет таким, какой он есть. просто добавьте одну строку ниже всех аргументов, присутствующих в аргументах VM, как -Xms512m -Xmx512m -XX: MaxPermSize = ... m (192m).

Я думаю, что это будет работать ...

Soumya Sandeep Mohanty
источник
7

Если вам необходимо отслеживать использование памяти во время выполнения, java.lang.managementпакет предлагает предложения, MBeansкоторые можно использовать для мониторинга пулов памяти в вашей виртуальной машине (например, пространства eden, генерации с постоянным доступом и т. Д.), А также поведения сборки мусора.

Свободное пространство кучи, сообщаемое этими MBean-компонентами, будет сильно различаться в зависимости от поведения GC, особенно если ваше приложение генерирует много объектов, которые позже будут обработаны GC-ed. Один из возможных подходов заключается в мониторинге свободного пространства кучи после каждого полного GC, который вы можете использовать для принятия решения об освобождении памяти путем сохранения объектов.

В конечном итоге, лучше всего ограничить сохранение памяти по мере возможности, пока производительность остается приемлемой. Как отмечалось в предыдущем комментарии, память всегда ограничена, но в вашем приложении должна быть стратегия борьбы с исчерпанием памяти.

Leigh
источник
5

Обратите внимание, что если вам это нужно в ситуации развертывания, рассмотрите возможность использования Java WebStart (с версией «ondisk», а не с сетевой версией - возможно в Java 6u10 и более поздних версиях), поскольку она позволяет указывать различные аргументы JVM в перекрестном Платформа способ.

В противном случае вам понадобится средство запуска, специфичное для операционной системы, которое устанавливает необходимые аргументы.

Турбьерн Равн Андерсен
источник
Java WebStart постепенно прекращается. Я еще не знаю о подходящей замене.
Турбьёрн Равн Андерсен
1

Если эта проблема возникает в Wildfly 8 и JDK1.8, тогда нам нужно указать настройки MaxMetaSpace вместо настроек PermGen.

Например, нам нужно добавить ниже конфигурацию в setenv.sh файл wildfly. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Для получения дополнительной информации, пожалуйста, проверьте Wildfly Heap Issue

Сатиш
источник
1

Что касается NetBeans, вы можете установить максимальный размер кучи для решения проблемы.

Перейдите в «Выполнить», затем -> «Установить конфигурацию проекта» -> «Настроить» -> «Выполнить» его всплывающего окна -> «Вариант VM» -> заполнить «-Xms2048m -Xmx2048m» ,

Сяоган
источник
1

Если вы продолжаете выделять и хранить ссылки на объект, вы будете заполнять любой объем памяти, который у вас есть.

Одним из вариантов является закрытие и открытие прозрачного файла, когда они переключают вкладки (вы сохраняете только указатель на файл, а когда пользователь переключает вкладку, вы закрываете и очищаете все объекты ... это замедляет изменение файла ... но ...) и, возможно, хранит только 3 или 4 файла в памяти.

Другая вещь, которую вы должны сделать, это когда пользователь открывает файл, загружает его и перехватывает любую ошибку OutOfMemoryError, затем (поскольку открыть файл невозможно) закрывает этот файл, очищает его объекты и предупреждает пользователя, что он должен закрыть неиспользуемый файл. файлы.

Ваша идея динамического расширения виртуальной памяти не решает проблему, поскольку машина ограничена в ресурсах, поэтому вы должны быть осторожны и решать проблемы с памятью (или, по крайней мере, быть осторожными с ними).

Несколько советов, которые я видел с утечками памяти:

-> Имейте в виду, что если вы положили что-то в коллекцию и впоследствии забыли об этом, у вас все еще есть сильная ссылка на нее, поэтому аннулируйте коллекцию, очистите ее или сделайте что-нибудь с ней ... если нет, вы найдете утечку памяти трудно найти.

-> Возможно, использование коллекций со слабыми ссылками (weakhashmap ...) может помочь с проблемами памяти, но вы должны быть осторожны с этим, поскольку вы можете обнаружить, что объект, который вы ищете, был собран.

-> Еще одна идея, которую я нашел, - разработать постоянную коллекцию, которая хранится в наименее используемых и прозрачно загруженных объектах базы данных. Это, вероятно, будет лучшим подходом ...

SoulWanderer
источник
0

Если ничего не помогает, в дополнение к увеличению максимального размера кучи попробуйте также увеличить размер подкачки. Для Linux соответствующие инструкции можно найти в https://linuxize.com/post/create-a-linux-swap-file/ .

Это может помочь, например, если вы собираете что-то большое во встроенной платформе.

NCCC
источник