java.lang.OutOfMemoryError: Превышен лимит накладных расходов GC - PullRequest
317 голосов
/ 30 апреля 2011

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

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

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

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

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

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

Эта проблема также обсуждается в связанной теме в StackOverflow.

Ответы [ 16 ]

157 голосов
/ 30 апреля 2011

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

  1. Укажите больше памяти, как вы упомянули, попробуйте что-то среднее, например -Xmx512m first
  2. Работа с меньшими партиями HashMap объектов для одновременной обработкиесли возможно
  3. Если у вас много повторяющихся строк, используйте String.intern(), прежде чем помещать их в HashMap
  4. Используйте HashMap(int initialCapacity, float loadFactor) конструктор для настройки для вашего случая
60 голосов
/ 22 ноября 2015

Следующее сработало для меня. Просто добавьте следующий фрагмент:

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"
    }
}
42 голосов
/ 03 февраля 2012

@ takrl: настройка по умолчанию для этой опции:

java -XX:+UseConcMarkSweepGC

, что означает, что эта опция по умолчанию не активна. Поэтому, когда вы говорите, что использовали опцию "+XX:UseConcMarkSweepGC" Я предполагаю, что вы использовали этот синтаксис:

java -XX:+UseConcMarkSweepGC

, что означает, что вы явно активировали эту опцию. Для правильного синтаксиса и настроек по умолчанию Java HotSpot VM Options @ это документ

23 голосов
/ 12 октября 2011

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

-XX:-UseConcMarkSweepGC

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

12 голосов
/ 30 апреля 2011

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

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

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

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

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

9 голосов
/ 28 января 2013

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

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

Если все сделано правильно, ваш алгоритм не должен даже замечать изменения - правильно здесь означает использовать класс для представления таблицы, даже давая ему методы put (key, value) и get (key), как в hashmap.

Когда промежуточная таблица заполнена, сгенерируйте необходимые операторы SQL из нее, а не из памяти.

9 голосов
/ 17 января 2013

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

8 голосов
/ 25 марта 2015

Параллельный сборщик сгенерирует OutOfMemoryError, если слишком много времени тратится на сборку мусора.В частности, если на сборку мусора уходит более 98% общего времени и восстанавливается менее 2% кучи, будет выброшено OutOfMemoryError.Эта функция предназначена для предотвращения запуска приложений в течение длительного периода времени при небольшом прогрессе или его отсутствии, поскольку куча слишком мала.При необходимости эту функцию можно отключить, добавив параметр -XX:-UseGCOverheadLimit в командную строку.

5 голосов
/ 12 августа 2016

Если у вас есть 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-строка-дедупликации /

5 голосов
/ 30 апреля 2011

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

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

...