Помилка java.lang.OutOfMemoryError: перевищено накладний ліміт GC


804

Я отримую це повідомлення про помилку під час виконання тестів JUnit:

java.lang.OutOfMemoryError: GC overhead limit exceeded

Я знаю, що OutOfMemoryErrorтаке, але що означає накладний ліміт ГК? Як я можу це вирішити?


16
Це звучить дуже цікаво. Я б хотів, якби хтось міг опублікувати якийсь код, який генерує це.
Бух

1
Я просто виявив проблему, яка призводить до занадто великого використання пам'яті, майже до межі купи. Простим рішенням може бути просто дати трохи більше Heap-пам’яті Java-Engine (-Xmx), але це допоможе лише в тому випадку, якщо програмі потрібно рівно стільки пам’яті, скільки було встановлено попереднє обмеження.
Мнемент

1
@Mnementh я дав відповідь тут перевірити , чи допомагає це stackoverflow.com/questions/11091516 / ...
Lulu

16
@SimonKuang Зауважте, що існує декілька OutOfMemoryErrorсценаріїв, для яких збільшення купи не є коректним рішенням: вичерпання натільних потоків і закінчення генеалогічної генерації (яка є окремою від купи) - два приклади. Будьте обережні, щоб робити занадто широкі заяви про OutOfMemoryErrors; є несподівано різноманітний набір речей, які можуть їх викликати.
Тім

3
Як ви вирішили питання ??
Thorsten Niehues

Відповіді:


763

Це повідомлення означає, що з якихось причин збирач сміття займає надмірну кількість часу (за замовчуванням 98% всього процесорного часу процесу) та відновлює дуже мало пам’яті за кожен пробіг (за замовчуванням 2% купи).

Це фактично означає, що ваша програма перестає робити будь-який прогрес і зайнята тим, що весь час займається лише збиранням сміття.

Щоб ваш додаток не забирав час процесора, не вчинивши нічого, JVM передає це Errorтак, щоб у вас був шанс діагностувати проблему.

Рідкісні випадки, коли я бачив це, - це те, що якийсь код створював тонни тимчасових об'єктів і тонни слабко посилальних об'єктів у вже дуже обмеженій пам'яті середовищі.

Перегляньте посібник з налаштування Java GC, який доступний для різних версій Java та містить розділи щодо цієї конкретної проблеми:


9
Чи правильно було б узагальнити свою відповідь так: "Це як помилка" Не в просторі купи Java ". Дайте їй більше пам'яті з -Xmx." ?
Тім Купер

58
@Tim: Ні, це не було б правильно. Хоча надання йому більше пам’яті може зменшити проблему, слід також переглянути свій код і побачити, чому він виробляє таку кількість сміття і чому ваш код проскакує трохи нижче позначки «поза пам'яттю». Часто це знак зламаного коду.
Йоахім Зауер

8
Дякую, схоже, Oracle насправді не такий хороший у міграції даних, вони перервали посилання.
Йоахім Зауер

151
Ви мали мене на "Спасибі, здається, Oracle насправді не такий хороший"
Роб Грант

3
@Guus: якщо кілька програм працюють в одному JVM, то так, вони можуть легко впливати один на одного. Важко буде сказати, хто з них погано поводиться. Поділ додатків на різні JVM може бути найпростішим рішенням.
Йоахім Зауер

215

Цитуючи статтю Oracle "Налаштування збирання сміття на віртуальній машині Java SE 6 HotSpot [tm]" :

Надмірний час GC та OutOfMemoryError

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

EDIT: схоже, хтось може набрати швидше, ніж я :)


87
"Ви можете вимкнути це ...", але ОП, швидше за все, не повинна цього робити.
Стівен C

2
Чи можете ви сказати мені різницю між "-XX" та "-Xmx"? Мені вдалося вимкнути його і за допомогою параметра "-Xmx".
Сушіель Джаваді

19
Відповідаю на дуже старий коментар тут, але ... @Bart На -XX:початку декількох параметрів командного рядка є своєрідний прапор, який вказує на те, що ця опція є високо VM-специфічною та нестабільною (може змінюватися без попереднього повідомлення у майбутніх версіях). У будь-якому випадку, -XX:-UseGCOverheadLimitпрапор каже ВМ відключити перевірку накладних лімітів GC (фактично "вимикає його"), тоді як ваша -Xmxкоманда просто збільшила купу. В останньому випадку перевірка накладних перевірок GC все ще виконується , це просто звучить, як більша купа вирішила проблеми з обмолотом GC у вашому випадку (це не завжди допоможе).
Анджей Дойль

1
У моїй програмі (читаючи великий файл Excel у Talend) це не спрацювало, і з інших пояснень користувачів я розумію, чому. Це просто вимикає помилку, але проблема зберігається, і ваша програма просто витратить більшу частину свого часу на обробку GC. Наш сервер мав багато оперативної пам’яті, тому я використав пропозиції Віталія, щоб збільшити розмір купи.
RobbZ

Ви зрештою отримаєте цю помилку, якщо ваш додаток буде інтенсивним використанням даних, очищення пам'яті та уникнення витоку даних - найкращий вихід - але це потребує певного часу.
П'євіс

89

Якщо ви впевнені, що у вашій програмі немає витоків пам'яті , спробуйте:

  1. Наприклад, збільште розмір купи -Xmx1g.
  2. Увімкніть паралельний колектор з низькою паузою -XX:+UseConcMarkSweepGC.
  3. Використовуйте існуючі об'єкти, коли можливо, щоб зберегти деяку кількість пам'яті.

При необхідності перевірку межі можна відключити, додавши параметр -XX:-UseGCOverheadLimitдо командного рядка.


9
Я не згоден з третьою порадою. Повторне використання існуючих об'єктів не економить пам'ять (не просочуйте старі об'єкти, економте пам'ять :-) Більше того, "повторне використання існуючого об'єкта" було практикою для зняття тиску GC. Але це ЗАВЖДИ не гарна ідея: із сучасними GC ми повинні уникати ситуацій, коли старі об’єкти містять нові, оскільки вони можуть порушити деякі припущення щодо місцевості ...
mcoolive

@mcoolive: Дещо надуманий приклад див. у коментарях до відповіді stackoverflow.com/a/5640498/4178262 нижче; створення Listоб'єкта всередині циклу викликало виклик GC 39 разів замість 22 рази.
Марк Стюарт

45

Зазвичай це код. Ось простий приклад:

import java.util.*;

public class GarbageCollector {

    public static void main(String... args) {

        System.out.printf("Testing...%n");
        List<Double> list = new ArrayList<Double>();
        for (int outer = 0; outer < 10000; outer++) {

            // list = new ArrayList<Double>(10000); // BAD
            // list = new ArrayList<Double>(); // WORSE
            list.clear(); // BETTER

            for (int inner = 0; inner < 10000; inner++) {
                list.add(Math.random());
            }

            if (outer % 1000 == 0) {
                System.out.printf("Outer loop at %d%n", outer);
            }

        }
        System.out.printf("Done.%n");
    }
}

Використання Java 1.6.0_24-b07 на Windows 7 32 біт.

java -Xloggc:gc.log GarbageCollector

Тоді подивіться gc.log

  • Запускається 444 рази методом BAD
  • Запускається 666 разів за допомогою методу WORSE
  • Запускається 354 рази за допомогою методу BETTER

Зараз надано, це не найкращий тест чи найкращий дизайн, але якщо ви стикаєтесь із ситуацією, коли у вас немає іншого вибору, окрім впровадження такого циклу або при роботі з існуючим кодом, який поводиться погано, вибір повторного використання об'єктів замість створення нових може зменшити скільки разів смітник збирається на шляху ...


12
Будь ласка, уточніть: Коли ви говорите "спрацьовано n разів", чи означає це, що звичайний GC траплявся n разів, або що помилка "перевищення граничного рівня GC", повідомлена ОП, сталася n разів?
Джон Шнайдер

Я тестував лише зараз, використовуючи java 1.8.0_91, і ніколи не отримував помилки / винятку, і "Спущено n разів" було від підрахунку кількості рядків у gc.logфайлі. Мої тести показують набагато менше разів загалом, але найменше "тригерів" часів НАЙКРАЩЕ, і тепер, BAD "гірше", ніж НАЙКРАЩІ зараз. Мої рахунки: БАД: 26, КОРОТА: 22, КРАЩЕ 21.
Марк Стюарт

Я просто додав «WORST_YET» модифікація , де я визначаю List<Double> listв зовнішньому контурі замість до того зовнішнього циклу, і Спрацьовує 39 сміття колекції.
Марк Стюарт

36

Причина помилки відповідно до платформи Java [8], Посібник з усунення неполадок у стандартній версії : (додано наголоси та розриви рядків)

[...] "Перевищений ліміт GC перевищує" вказує на те, що сміттєзбірник працює весь час, і програма Java робить дуже повільний прогрес.

Після збору сміття, якщо процес Java витрачає більше приблизно 98% свого часу на вивезення сміття, і якщо він відновлює менше 2% купи і до цього часу робив останні 5 (постійний час збирання) сміття поспіль колекції, то java.lang.OutOfMemoryErrorкидається а. [...]

  1. Збільшити розмір купи, якщо поточної купи недостатньо.
  2. Якщо ви все-таки отримаєте цю помилку після збільшення накопичувальної пам’яті, використовуйте засоби профілювання пам’яті , такі як MAT (інструмент аналізатора пам’яті), Visual VM тощо та виправити витоки пам’яті.
  3. Оновіть версію JDK до останньої версії (1.8.x) або принаймні 1.7.x та використовуйте алгоритм G1GC. . Мета пропускної здатності для G1 GC - це 90-відсотковий час застосування та 10-відсотковий час збору сміття
  4. Окрім встановлення пам’яті купи - Xms1g -Xmx2gспробуйте

    -XX:+UseG1GC -XX:G1HeapRegionSize=n -XX:MaxGCPauseMillis=m  
    -XX:ParallelGCThreads=n -XX:ConcGCThreads=n

Перегляньте ще кілька пов'язаних питань щодо G1GC


29

Просто трохи збільшить розмір купи, встановивши цю опцію в

Виконати → Запустити конфігурації → Аргументи → Аргументи VM

-Xms1024M -Xmx2048M

Xms - для мінімальної межі

Xmx - для максимальної межі


2
У додатках для Android немає argumentsвкладки ... що нам робити для досягнення цього?
Блейз Тама

3
Який інструмент відповідає на це? Це не було питання затемнення.
Майкл Пієфель

3
Не існує "мінімальної межі". -Xms - початковий розмір.
Дієго Квейроз

1
Який максимальний ліміт, який можна встановити ??
JPerk

14

Для мене спрацювали наступні кроки:

  1. Відкрийте eclipse.iniфайл
  2. Зміна

    -Xms40m
    -Xmx512m

    до

    -Xms512m
    -Xmx1024m
  3. Перезапустіть затемнення

Дивіться тут


найпростіший спосіб виправити цю проблему. Дякую :)
Хамза

1
Файл eclipse.ini в jdev?
Абхінаба Басу

проблеми не вирішені навіть тоді, коли конфігурація була змінена на цю.
zionpi

1
ОП не задавало питання затемнення.
Майкл Пієфель

1
Ця "відповідь" не відповідає на запитання вище.
Freitags

13

спробуйте це

відкрити build.gradleфайл

  android {
        dexOptions {
           javaMaxHeapSize = "4g"
        }
   }

Відмінно працює для тренажера. Будь-яка ідея, як це впливає на реальні пристрої? тобто це гарна ідея чи це просто маскування питання? Дякую.
Джошуа Пінтер

11

Наступне працювало для мене. Просто додайте такий фрагмент:

android {
        compileSdkVersion 25
        buildToolsVersion '25.0.1'

defaultConfig {
        applicationId "yourpackage"
        minSdkVersion 10
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }
dexOptions {
        javaMaxHeapSize "4g"
    }
}

Так, при використанні Gradle :)
Alex

4
Як ти міг навіть подумати , що це рішення його питання в цілому ? Ви встановлюєте розмір купи в 4g, що абсолютно довільно в конфігурації gradle для Android facepalm .
Джуліан Л.

7

збільшити javaMaxHeapsize у файлі build.gradle (Модуль: додаток)

dexOptions {
    javaMaxHeapSize "1g"
}

до (Додати цей рядок у gradle)

 dexOptions {
        javaMaxHeapSize "4g"
    }

3

Описи розміру кучі Java (xms, xmx, xmn)

-Xms size in bytes

Example : java -Xms32m

Встановлює початковий розмір купи Java. За замовчуванням розмір - 2097152 (2 МБ). Значення повинні бути кратними та більшими 1024 байт (1 КБ). (Прапор -server збільшує розмір за замовчуванням до 32М.)

-Xmn size in bytes

Example : java -Xmx2m

Встановлює початковий розмір кучі Java для покоління Eden. Значення за замовчуванням - 640K. (Прапор -server збільшує розмір за замовчуванням до 2М.)

-Xmx size in bytes

Example : java -Xmx2048m

Встановлює максимальний розмір, до якого може виростати купа Java. Типовий розмір - 64М. (Прапор -server збільшує розмір за замовчуванням до 128M.) Максимальна межа купи - близько 2 ГБ (2048 МБ).

Форматування аргументів пам'яті Java (xms, xmx, xmn)

Встановлюючи розмір купи Java, слід вказати аргумент пам’яті, використовуючи одну з літер «m» або «M» для MB, або «g» або «G» для GB. Налаштування не працюватиме, якщо ви вкажете "MB" або "GB". Дійсні аргументи виглядають приблизно так:

-Xms64m або -Xms64M -Xmx1g або -Xmx1G Також можна використовувати 2048MB для вказівки 2 Гб. Також обов’язково використовуйте цілі числа, коли вказуєте свої аргументи. Використання -Xmx512m є допустимим варіантом, але -Xmx0,5g призведе до помилки.

Ця довідка може бути корисною для когось.


2

Ви також можете збільшити розміщення пам'яті та розмір купи, додавши це у свій gradle.propertiesфайл:

org.gradle.jvmargs=-Xmx2048M -XX\:MaxHeapSize\=32g

Це не повинно бути 2048М і 32г, зробіть його таким же великим, як вам хочеться.


2

Вирішено:
Просто додайте
org.gradle.jvmargs=-Xmx1024m
в
gradle.properties
і якщо вона не існує, створіть його.


0

Я працюю в Android Studio і зіткнувся з цією помилкою, намагаючись створити підписаний APK для випуску. Мені вдалося створити і протестувати APK для налагодження без проблем, але як тільки я захотів створити APK для випуску, процес збірки запустився б кілька хвилин, а потім остаточно завершиться "Помилка java.lang.OutOfMemoryError: GC перевищена ліміт витрат ". Я збільшив розміри купи як для VM, так і для компілятора Android DEX, але проблема не зникала. Нарешті, через багато годин та гуртки кави виявилося, що проблема була у моєму файлі "build.gradle" на рівні програми - у мене був параметр "minifyEnabled" для типу збірки випуску встановлений на "false", отже, запущені матеріали Proguard щодо коду, який не пройшов процес зменшення коду '(див https://developer.android.). Я змінив параметр 'minifyEnabled' на 'true', і збірка релізу виконана як мрія :)

Коротше кажучи, мені довелося змінити файл "build.gradle" на рівні програми: // ...

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.sign_config_release
    }
    debug {
        debuggable true
        signingConfig signingConfigs.sign_config_debug
    }
}

//...

до

    //...

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.sign_config_release
    }
    debug {
        debuggable true
        signingConfig signingConfigs.sign_config_debug
    }
}

//...

0

Щоб збільшити розмір купи в IntelliJ IDEA, дотримуйтесь наступних інструкцій. Це працювало для мене.

Для користувачів Windows,

Перейдіть до місця, де встановлено IDE, і знайдіть наступне.

idea64.exe.vmoptions

Відредагуйте файл та додайте наступне.

-Xms512m
-Xmx2024m
-XX:MaxPermSize=700m
-XX:ReservedCodeCacheSize=480m

Це все !!


0

ви можете спробувати внести зміни в налаштування сервера, звернувшись до цього зображення та збільшити розмір пам'яті для змін процесу обробки, виділених жовтим кольором

Ви також можете внести зміни в java heap, відкривши cmd-> set _java_opts -Xmx2g
2g (2 гігабайти) залежно від складності вашої програми.

спробуйте використовувати менш постійні змінні та тимчасові змінні

введіть тут опис зображення


-1

Вам потрібно збільшити розмір пам'яті в 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**
)

і

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**

4
Ці налаштування стосуються лише вашої локальної IDE. Це не вплине на середовище Prod.
Фен

-1

У Netbeans може бути корисно розробити максимальний розмір купи. Перейдіть до запуску => Встановити конфігурацію проекту => Налаштувати . У вікні Виконання спливаючого вікна перейдіть до VM Option , заповніть -Xms2048m -Xmx2048m. Це може вирішити проблему розміру купи.


-1

Перезавантаження мого MacBook вирішило цю проблему для мене.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.