java.lang.OutOfMemoryError: Превышен лимит накладных расходов GC
Я получаю эту ошибку в программе, которая создает несколько (сотни тысяч) объектов HashMap с несколькими (15-20) текстовыми записями каждый. Эти строки должны быть собраны (без разбивки на меньшие суммы) перед отправкой в базу данных.
Согласно Sun, ошибка возникает, "если слишком много времени тратится на сборку мусора: если на сборку мусора уходит более 98% общего времени и восстанавливается менее 2% кучи, генерируется ошибка OutOfMemoryError.".
По-видимому, можно использовать командную строку для передачи аргументов в JVM для
- Увеличение размера кучи через "-Xmx1024m" (или более) или
- Отключение проверки ошибок в целом через -XX:-UseGCOverheadLimit.
Первый подход работает отлично, второй заканчивается в другом java.lang.OutOfMemoryError, на этот раз о куче.
Итак, вопрос: есть ли программная альтернатива этому для конкретного варианта использования (например, несколько небольших объектов HashMap)? Например, если я использую метод clear() HashMap, проблема исчезнет, но и данные, хранящиеся в HashMap!:-)
Эта проблема также обсуждается в соответствующей теме в Stackru.
16 ответов
По сути, вам не хватает памяти, чтобы запустить процесс гладко. Варианты, которые приходят на ум:
- Укажите больше памяти, как вы упомянули, попробуйте что-то среднее между, как
-Xmx512m
первый - Работа с небольшими партиями
HashMap
объекты для обработки сразу, если это возможно - Если у вас много повторяющихся строк, используйте
String.intern()
на них, прежде чем положить их вHashMap
- Использовать
HashMap(int initialCapacity, float loadFactor)
конструктор, чтобы настроить для вашего случая
Следующее сработало для меня. Просто добавьте следующий фрагмент:
dexOptions {
javaMaxHeapSize "4g"
}
На ваш build.gradle
:
android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
defaultConfig {
applicationId "yourpackage"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
}
dexOptions {
javaMaxHeapSize "4g"
}
}
@takrl: настройка по умолчанию для этой опции:
java -XX:+UseConcMarkSweepGC
Это означает, что эта опция не активна по умолчанию. Поэтому, когда вы говорите, что использовали опцию "+XX:UseConcMarkSweepGC
"Я предполагаю, что вы использовали этот синтаксис:
java -XX:+UseConcMarkSweepGC
Это означает, что вы явно активировали эту опцию. Для правильного синтаксиса и настроек по умолчанию Java HotSpot VM Options
@ этот документ
К слову, сегодня у нас была такая же проблема. Мы исправили это с помощью этой опции:
-XX:-UseConcMarkSweepGC
По-видимому, это изменило стратегию, используемую для сбора мусора, что привело к исчезновению проблемы.
Хммм... вам либо нужно:
Полностью переосмыслите свой алгоритм и структуры данных, чтобы не нуждаться во всех этих маленьких хэш-картах.
Создайте фасад, который позволит вам выстраивать эти HashMaps по мере необходимости. Простой LRU-кеш может быть просто билетом.
Увеличьте объем памяти, доступной для JVM. Если необходимо, даже покупка большего объема ОЗУ может быть самым быстрым и дешевым решением, если у вас есть управление машиной, на которой находится этот зверь. Сказав это: я, как правило, не фанат решений "брось больше оборудования", особенно если альтернативное алгоритмическое решение может быть найдено в разумные сроки. Если вы продолжаете бросать больше оборудования на каждую из этих проблем, вы скоро столкнетесь с законом убывающей отдачи.
Что ты вообще пытаешься делать? Я подозреваю, что есть лучший подход к вашей актуальной проблеме.
Не храните всю структуру в памяти в ожидании завершения.
Записывать промежуточные результаты во временную таблицу в базе данных вместо хеш-таблиц - функционально таблица базы данных является эквивалентом хеш-карты, то есть обе поддерживают доступ к данным с помощью ключа, но таблица не связана с памятью, поэтому используйте здесь индексированную таблицу, а не хешмапы.
Если все сделано правильно, ваш алгоритм не должен даже замечать изменения - правильно здесь означает использовать класс для представления таблицы, даже давая ему методы put(key, value) и get(key), как в hashmap.
Когда промежуточная таблица заполнена, сгенерируйте необходимые операторы SQL из нее, а не из памяти.
Параллельный коллектор бросит OutOfMemoryError
если слишком много времени тратится на сборку мусора. В частности, если на сборку мусора уходит более 98% общего времени и восстанавливается менее 2% кучи, OutOfMemoryError
будет брошен. Эта функция предназначена для предотвращения запуска приложений в течение длительного периода времени при небольшом прогрессе или его отсутствии, поскольку куча слишком мала. При необходимости эту функцию можно отключить, добавив параметр -XX:-UseGCOverheadLimit
в командной строке.
Если у вас есть java8, и вы можете использовать сборщик мусора G1, запустите ваше приложение с помощью:
-XX:+UseG1GC -XX:+UseStringDeduplication
Это говорит G1 о том, чтобы найти похожие строки и сохранить в памяти только одну из них, а остальные - только указатель на эту строку в памяти.
Это полезно, когда у вас много повторяющихся строк. Это решение может работать или не работать и зависит от каждого приложения.
Больше информации о:
https://blog.codecentric.de/en/2014/08/string-deduplication-new-feature-java-8-update-20-2/ http://java-performance.info/java-string-deduplication/
Если вы создаете сотни тысяч хеш-карт, вы, вероятно, используете гораздо больше, чем вам действительно нужно; если вы не работаете с большими файлами или графикой, хранение простых данных не должно превышать лимит памяти Java.
Вы должны попытаться переосмыслить свой алгоритм. В этом случае я бы предложил больше помощи по этому вопросу, но не могу дать никакой информации, пока вы не предоставите больше информации о контексте проблемы.
Для этого используйте приведенный ниже код в файле приложения для приложения под закрытием Android.
dexOptions {javaMaxHeapSize "4g"}
Устраните утечки памяти в вашем приложении с помощью инструментов профиля, таких как eclipse MAT или VisualVM
С JDK 1.7.x
или более поздние версии, используйте G1GC
, который тратит 10% на сборку мусора в отличие от 2% в других алгоритмах GC.
Помимо установки кучи памяти с -Xms1g -Xmx2g
попробуйте
-XX:+UseG1GC
-XX:G1HeapRegionSize=n,
-XX:MaxGCPauseMillis=m,
-XX:ParallelGCThreads=n,
-XX:ConcGCThreads=n`
Взгляните на статью оракула для тонкой настройки этих параметров.
Некоторые вопросы, связанные с G1GC в SE:
Java 7 (JDK 7) сборка мусора и документация по G1
Вам нужно увеличить размер памяти в Jdeveloper, перейти в setDomainEnv.cmd.
set WLS_HOME=%WL_HOME%\server
set XMS_SUN_64BIT=256
set XMS_SUN_32BIT=256
set XMX_SUN_64BIT=3072
set XMX_SUN_32BIT=3072
set XMS_JROCKIT_64BIT=256
set XMS_JROCKIT_32BIT=256
set XMX_JROCKIT_64BIT=1024
set XMX_JROCKIT_32BIT=1024
if "%JAVA_VENDOR%"=="Sun" (
set WLS_MEM_ARGS_64BIT=-Xms256m -Xmx512m
set WLS_MEM_ARGS_32BIT=-Xms256m -Xmx512m
) else (
set WLS_MEM_ARGS_64BIT=-Xms512m -Xmx512m
set WLS_MEM_ARGS_32BIT=-Xms512m -Xmx512m
)
and
set MEM_PERM_SIZE_64BIT=-XX:PermSize=256m
set MEM_PERM_SIZE_32BIT=-XX:PermSize=256m
if "%JAVA_USE_64BIT%"=="true" (
set MEM_PERM_SIZE=%MEM_PERM_SIZE_64BIT%
) else (
set MEM_PERM_SIZE=%MEM_PERM_SIZE_32BIT%
)
set MEM_MAX_PERM_SIZE_64BIT=-XX:MaxPermSize=1024m
set MEM_MAX_PERM_SIZE_32BIT=-XX:MaxPermSize=1024m
В случае ошибки:
"Внутренняя ошибка компилятора: java.lang.OutOfMemoryError: Превышен предел издержек GC на java.lang.AbstractStringBuilder"
увеличить пространство кучи Java до 2 ГБ, т.е. -Xmx2g.
Для моего случая увеличение памяти с помощью -Xmx
вариант был решением.
Я прочитал файл 10g в Java, и каждый раз получал одну и ту же ошибку. Это произошло, когда значение в RES
колонка в top
Команда достигла значения, установленного в опции -Xmx. Затем путем увеличения памяти с помощью -Xmx
Вариант все прошло нормально.
Был еще один момент. Когда я установил JAVA_OPTS
или же CATALINA_OPTS
в моей учетной записи пользователя и увеличил объем памяти снова я получил ту же ошибку. Затем я напечатал значение этих переменных окружения в моем коде, которое дало мне значения, отличные от установленных. Причина была в том, что Tomcat был корнем этого процесса, а затем, так как я не был неудачником, я попросил администратора увеличить память в catalina.sh
в Tomcat.
Это помогло мне избавиться от этой ошибки. Этот параметр отключает -XX:+DisableExplicitGC