java.lang.OutOfMemoryError: Превышен лимит накладных расходов GC

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

Согласно Sun, ошибка возникает, "если слишком много времени тратится на сборку мусора: если на сборку мусора уходит более 98% общего времени и восстанавливается менее 2% кучи, генерируется ошибка OutOfMemoryError.".

По-видимому, можно использовать командную строку для передачи аргументов в JVM для

  • Увеличение размера кучи через "-Xmx1024m" (или более) или
  • Отключение проверки ошибок в целом через -XX:-UseGCOverheadLimit.

Первый подход работает отлично, второй заканчивается в другом java.lang.OutOfMemoryError, на этот раз о куче.

Итак, вопрос: есть ли программная альтернатива этому для конкретного варианта использования (например, несколько небольших объектов HashMap)? Например, если я использую метод clear() HashMap, проблема исчезнет, ​​но и данные, хранящиеся в HashMap!:-)

Эта проблема также обсуждается в соответствующей теме в Stackru.

16 ответов

Решение

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

  1. Укажите больше памяти, как вы упомянули, попробуйте что-то среднее между, как -Xmx512m первый
  2. Работа с небольшими партиями HashMap объекты для обработки сразу, если это возможно
  3. Если у вас много повторяющихся строк, используйте String.intern() на них, прежде чем положить их в HashMap
  4. Использовать 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

По-видимому, это изменило стратегию, используемую для сбора мусора, что привело к исчезновению проблемы.

Хммм... вам либо нужно:

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

  2. Создайте фасад, который позволит вам выстраивать эти HashMaps по мере необходимости. Простой LRU-кеш может быть просто билетом.

  3. Увеличьте объем памяти, доступной для JVM. Если необходимо, даже покупка большего объема ОЗУ может быть самым быстрым и дешевым решением, если у вас есть управление машиной, на которой находится этот зверь. Сказав это: я, как правило, не фанат решений "брось больше оборудования", особенно если альтернативное алгоритмическое решение может быть найдено в разумные сроки. Если вы продолжаете бросать больше оборудования на каждую из этих проблем, вы скоро столкнетесь с законом убывающей отдачи.

Что ты вообще пытаешься делать? Я подозреваю, что есть лучший подход к вашей актуальной проблеме.

Используйте альтернативную реализацию HashMap ( Trove). Стандартный Java HashMap имеет>12-кратное использование памяти. Подробности можно прочитать здесь.

Не храните всю структуру в памяти в ожидании завершения.

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

Если все сделано правильно, ваш алгоритм не должен даже замечать изменения - правильно здесь означает использовать класс для представления таблицы, даже давая ему методы 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

Сборка мусора Java 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

Другие вопросы по тегам