Błąd java.lang.OutOfMemoryError: Przekroczono limit obciążenia GC

804

Podczas wykonywania testów JUnit pojawia się ten komunikat o błędzie:

java.lang.OutOfMemoryError: GC overhead limit exceeded

Wiem, co to OutOfMemoryErrorjest, ale co oznacza limit napowietrzny GC? Jak mogę to rozwiązać?

Memento
źródło
16
Brzmi bardzo interesująco. Chciałbym, aby ktoś mógł opublikować kod, który to generuje.
Buhb
1
Po prostu znalazłem problem, który prowadzi do zbyt dużego zużycia pamięci, blisko granicy sterty. Prostym rozwiązaniem może być po prostu dodanie większej ilości pamięci sterty do silnika Java (-Xmx), ale pomaga to tylko wtedy, gdy aplikacja potrzebuje dokładnie tyle pamięci, ile wcześniej ustawiono limit sterty.
Mnementh
1
@Mnementh dałem tutaj odpowiedź, sprawdź, czy to pomaga stackoverflow.com/questions/11091516/…
lulu
16
@ SimonKuang Należy pamiętać, że istnieje wiele OutOfMemoryErrorscenariuszy, dla których zwiększenie sterty nie jest prawidłowym rozwiązaniem: wyczerpanie wątków natywnych i wyczerpanie uprawnień generatora (które jest oddzielne od stosu) to dwa przykłady. Zachowaj ostrożność przy składaniu zbyt ogólnych oświadczeń na temat OutOfMemoryErrors; istnieje nieoczekiwanie różnorodny zestaw rzeczy, które mogą je powodować.
Tim
3
Jak rozwiązałeś problem?
Thorsten Niehues

Odpowiedzi:

763

Ten komunikat oznacza, że ​​z jakiegoś powodu śmieciarz zajmuje zbyt dużo czasu (domyślnie 98% całego czasu procesora w procesie) i odzyskuje bardzo mało pamięci przy każdym uruchomieniu (domyślnie 2% sterty).

Oznacza to skutecznie, że Twój program przestaje robić postępy i jest cały czas zajęty tylko wyrzucaniem elementów bezużytecznych.

JVM rzuca to, Erroraby Twoja aplikacja nie pochłaniała czasu procesora bez wykonywania czegokolwiek, dzięki czemu masz szansę zdiagnozować problem.

Rzadkie przypadki, w których to widziałem, polegały na tym, że jakiś kod tworzył mnóstwo tymczasowych obiektów i ton słabo przywoływanych obiektów w już bardzo ograniczonym pamięci.

Sprawdź przewodnik strojenia Java GC, który jest dostępny dla różnych wersji Java i zawiera sekcje dotyczące tego konkretnego problemu:

Joachim Sauer
źródło
9
Czy poprawne byłoby podsumowanie odpowiedzi w następujący sposób: „To tak jak błąd„ Brak miejsca w stosie Java ”. Daj mu więcej pamięci dzięki -Xmx.” ?
Tim Cooper
58
@Tim: Nie, to nie byłoby poprawne. Dając mu więcej pamięci mogłaby zmniejszyć problem, należy również spojrzeć na kod i zobaczyć, dlaczego to wytwarza taką ilość śmieci i dlaczego twoi skims kod tuż poniżej „out of memory” znak. Często jest to znak zepsutego kodu.
Joachim Sauer
8
Dzięki, wygląda na to, że Oracle nie jest tak dobry w migracji danych, zerwali link.
Joachim Sauer
151
Miałeś mnie na „Dzięki, wygląda na to, że Oracle nie jest wcale taka dobra”
Rob Grant
3
@Guus: jeśli wiele aplikacji działa w tej samej JVM, to tak, mogą łatwo wpływać na siebie nawzajem. Trudno będzie stwierdzić, który z nich źle się zachowuje. Rozdzielenie aplikacji na odrębne maszyny JVM może być najłatwiejszym rozwiązaniem.
Joachim Sauer
215

Cytując z artykułu Oracle „Java SE 6 HotSpot [tm] Tuning czyszczenia maszyn wirtualnych” :

Nadmierny czas GC i OutOfMemoryError

Kolektor równoległy wyrzuci błąd OutOfMemoryError, jeśli zbyt dużo czasu jest poświęcane na wyrzucanie elementów bezużytecznych: jeśli więcej niż 98% całkowitego czasu jest spędzane na zbieraniu elementów bezużytecznych i odzyskane jest mniej niż 2% sterty, zostanie wyrzucony błąd OutOfMemoryError. Ta funkcja ma na celu zapobieganie uruchamianiu aplikacji przez dłuższy czas przy niewielkim lub zerowym postępie, ponieważ sterty są zbyt małe. W razie potrzeby tę funkcję można wyłączyć, dodając opcję -XX:-UseGCOverheadLimitdo wiersza polecenia.

EDYCJA: wygląda na to, że ktoś może pisać szybciej niż ja :)

Dave
źródło
87
„Możesz to wyłączyć ...”, ale OP najprawdopodobniej nie powinien tego robić.
Stephen C
2
Czy możesz mi powiedzieć różnicę między „-XX” a „-Xmx”? Byłem w stanie to wyłączyć również przy użyciu opcji „-Xmx”.
Susheel Javadi
19
W odpowiedzi na bardzo stary komentarz tutaj, ale ... @Bart Na -XX:początku kilku opcji wiersza polecenia jest flagą, wskazującą, że ta opcja jest wysoce specyficzna dla maszyny wirtualnej i niestabilna (może ulec zmianie bez powiadomienia w przyszłych wersjach). W każdym razie -XX:-UseGCOverheadLimitflaga informuje maszynę wirtualną, aby wyłączyła sprawdzanie limitu narzutu GC (faktycznie „wyłącza”), podczas gdy twoje -Xmxpolecenie jedynie zwiększyło stos. W tym drugim przypadku sprawdzanie narzutu GC nadal działało , to po prostu brzmi jak większa kupa rozwiązała problemy z przerzucaniem GC w twoim przypadku (nie zawsze to pomoże).
Andrzej Doyle
1
W mojej aplikacji (czytanie dużego pliku Excela w Talend) to nie działało i z wyjaśnień innych użytkowników rozumiem dlaczego. To po prostu wyłącza błąd, ale problem nadal występuje, a aplikacja po prostu spędza większość czasu na obsłudze GC. Nasz serwer miał dużo pamięci RAM, więc skorzystałem z sugestii Vitalii, aby zwiększyć rozmiar sterty.
RobbZ
Ten błąd pojawi się w końcu, jeśli aplikacja wymaga dużej ilości danych, najlepszym rozwiązaniem jest wyczyszczenie pamięci i uniknięcie wycieku danych - ale wymaga to trochę czasu.
Pievis
89

Jeśli masz pewność, że w twoim programie nie ma wycieków pamięci , spróbuj:

  1. Na przykład zwiększ rozmiar sterty -Xmx1g.
  2. Włącz równoległy kolektor niskich przerw -XX:+UseConcMarkSweepGC.
  3. Użyj ponownie istniejących obiektów, jeśli to możliwe, aby zaoszczędzić trochę pamięci.

W razie potrzeby kontrolę limitu można wyłączyć, dodając opcję -XX:-UseGCOverheadLimitdo wiersza poleceń.

Vitalii Fedorenko
źródło
9
Nie zgadzam się z trzecią radą. Ponowne użycie istniejących obiektów nie oszczędza pamięci (nie wyciekaj starych obiektów, oszczędzaj pamięć :-) Ponadto „ponowne użycie istniejącego obiektu” było praktyką zmniejszania ciśnienia GC. Ale to NIE ZAWSZE jest dobry pomysł: przy nowoczesnym GC powinniśmy unikać sytuacji, w których stare obiekty zawierają nowe, ponieważ mogą one złamać pewne założenia dotyczące lokalizacji ...
mcoolive
@mcoolive: nieco wymyślony przykład znajduje się w komentarzach do odpowiedzi stackoverflow.com/a/5640498/4178262 poniżej; utworzenie Listobiektu wewnątrz pętli spowodowało wywołanie GC 39 razy zamiast 22 razy.
Mark Stewart
45

Zwykle jest to kod. Oto prosty przykład:

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");
    }
}

Korzystanie z Java 1.6.0_24-b07 w 32-bitowym systemie Windows 7.

java -Xloggc:gc.log GarbageCollector

Następnie spójrz na gc.log

  • Wywołano 444 razy przy użyciu metody BAD
  • Uruchomiono 666 razy przy użyciu metody WORSE
  • Wywołano 354 razy przy użyciu LEPSZEJ metody

Przyznaję, że nie jest to najlepszy test ani najlepszy projekt, ale w obliczu sytuacji, w której nie masz innego wyboru, jak zaimplementować taką pętlę lub gdy masz do czynienia z istniejącym kodem, który źle się zachowuje, wybór ponownego użycia obiektów zamiast tworzenia nowych może zmniejszyć ile razy kolektor śmieci przeszkadza ...

Mikrofon
źródło
12
Wyjaśnij: Kiedy mówisz „Wyzwolony n razy”, czy to oznacza, że ​​n razy miało miejsce normalne GC, czy też błąd „Przekroczony limit narzutu GC” zgłoszony przez OP n razy?
Jon Schneider,
Właśnie przetestowałem przy użyciu java 1.8.0_91 i nigdy nie otrzymałem błędu / wyjątku, a „Wyzwolone n razy” polegało na zliczaniu liczby linii w gc.logpliku. Moje testy pokazują znacznie mniej razy ogólnie, ale najmniej razy „wyzwalaczy” dla LEPSZEJ, a teraz BAD jest „gorszy” niż WORST teraz. Moje liczby: ZŁE: 26, GORNE: 22, LEPSZE 21.
Mark Stewart
Właśnie dodano „WORST_YET” modyfikacja gdzie zdefiniować List<Double> listw zewnętrznej pętli , a nie przed zewnętrznej pętli i wyzwalane 39 zbiory śmieci.
Mark Stewart
36

Przyczyna błędu według platformy Java [8], Podręcznik rozwiązywania problemów w edycji standardowej : (wyróżnienie i podział wierszy dodane)

[...] „Przekroczono limit narzutu GC” wskazuje, że moduł wyrzucający śmieci działa cały czas, a program Java robi bardzo wolne postępy.

Po wyrzucaniu elementów bezużytecznych, jeśli proces Java spędza więcej niż około 98% swojego czasu na zbieraniu elementów bezużytecznych i jeśli odzyskuje mniej niż 2% sterty i do tej pory robił ostatnie 5 (stała czasowa kompilacji) kolejnych śmieci kolekcje, a następnie java.lang.OutOfMemoryErrorwyrzuca się. [...]

  1. Zwiększ rozmiar sterty, jeśli bieżąca sterty nie jest wystarczająca.
  2. Jeśli nadal występuje ten błąd po zwiększeniu pamięci sterty, użyj narzędzi do profilowania pamięci , takich jak MAT (narzędzie do analizy pamięci), Visual VM itp. I napraw wycieki pamięci.
  3. Zaktualizuj wersję JDK do najnowszej wersji (1.8.x) lub co najmniej 1.7.xi użyj algorytmu G1GC. . Celem przepustowości G1 GC jest 90 procent czasu aplikacji i 10 procent czasu usuwania śmieci
  4. Oprócz ustawienia pamięci sterty za pomocą - Xms1g -Xmx2gspróbuj

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

Zapoznaj się z kilkoma powiązanymi pytaniami dotyczącymi G1GC

Ravindra babu
źródło
29

Po prostu zwiększ nieco rozmiar sterty, ustawiając tę ​​opcję w

Uruchom → Uruchom konfiguracje → Argumenty → Argumenty VM

-Xms1024M -Xmx2048M

Xms - dla minimalnego limitu

Xmx - dla maksymalnego limitu

kotlety
źródło
2
Aplikacje na Androida nie mają argumentskarty ... co powinniśmy zrobić, aby to osiągnąć?
Blaze Tama,
3
Na jakie narzędzie jest ta odpowiedź? To nie było pytanie o Zaćmienie.
Michael Piefel,
3
Nie ma „minimalnego limitu”. -Xms to rozmiar początkowy.
Diego Queiroz,
1
Jaki jest maksymalny limit, który można ustawić?
JPerk
14

Dla mnie zadziałały następujące kroki:

  1. Otwórz eclipse.iniplik
  2. Zmiana

    -Xms40m
    -Xmx512m

    do

    -Xms512m
    -Xmx1024m
  3. Uruchom ponownie Eclipse

Spójrz tutaj

Sunil Kumar Sahoo
źródło
najprostszy sposób na rozwiązanie tego problemu. Dzięki :)
Hamza
1
plik eclipse.ini w jdev?
Abhinaba Basu
problemy nierozwiązane, nawet jeśli konfiguracja została zmieniona na to.
zionpi 19.04.16
1
OP nie zadał pytania o zaćmienie.
Michael Piefel,
1
Ta „odpowiedź” nie odpowiada na powyższe pytanie.
Freitags,
13

Spróbuj tego

otwórz build.gradleplik

  android {
        dexOptions {
           javaMaxHeapSize = "4g"
        }
   }
alicanozkara
źródło
Działa świetnie w symulatorze. Wiesz, jak to wpływa na rzeczywiste urządzenia? tzn. czy to dobry pomysł, czy tylko maskowanie problemu? Dzięki.
Joshua Pinter
11

Poniższe działało dla mnie. Po prostu dodaj następujący fragment kodu:

android {
        compileSdkVersion 25
        buildToolsVersion '25.0.1'

defaultConfig {
        applicationId "yourpackage"
        minSdkVersion 10
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }
dexOptions {
        javaMaxHeapSize "4g"
    }
}
Hoshouny
źródło
Tak, używając Gradle :)
Alex
4
Jak można nawet, że jest to rozwiązanie na jego pytanie w ogóle ? Ustawić rozmiar sterty 4G, który jest całkowicie arbitralna w Gradle konfiguracji dla Androida Facepalm .
Julian L.,
7

zwiększ javaMaxHeapsize w pliku build.gradle (moduł: aplikacja)

dexOptions {
    javaMaxHeapSize "1g"
}

do (Dodaj tę linię w gradiencie)

 dexOptions {
        javaMaxHeapSize "4g"
    }
Sai Gopi N.
źródło
3

Opisy rozmiarów sterty Java (xms, xmx, xmn)

-Xms size in bytes

Example : java -Xms32m

Ustawia początkowy rozmiar sterty Java. Domyślny rozmiar to 2097152 (2 MB). Wartości muszą być wielokrotnością 1024 bajtów (1 KB) i być większe. (Flaga -server zwiększa domyślny rozmiar do 32 M.)

-Xmn size in bytes

Example : java -Xmx2m

Ustawia początkowy rozmiar sterty Java dla generacji Eden. Wartość domyślna to 640 KB. (Flaga -server zwiększa domyślny rozmiar do 2M).

-Xmx size in bytes

Example : java -Xmx2048m

Ustawia maksymalny rozmiar, do którego może wzrosnąć sterta Java. Domyślny rozmiar to 64 M. (Flaga -server zwiększa domyślny rozmiar do 128M.) Maksymalny limit sterty wynosi około 2 GB (2048 MB).

Formatowanie argumentów pamięci Java (xms, xmx, xmn)

Podczas ustawiania wielkości sterty Java należy podać argument pamięci, używając jednej z liter „m” lub „M” dla MB, lub „g” lub „G” dla GB. Twoje ustawienie nie będzie działać, jeśli podasz „MB” lub „GB”. Prawidłowe argumenty wyglądają tak:

-Xms64m lub -Xms64M -Xmx1g lub -Xmx1G Można również użyć 2048 MB, aby określić 2 GB Ponadto, należy podać liczby całkowite przy podawaniu argumentów. Użycie -Xmx512m jest prawidłową opcją, ale -Xmx0,5g spowoduje błąd.

To odniesienie może być pomocne dla kogoś.

Feniks
źródło
2

Możesz także zwiększyć przydział pamięci i rozmiar sterty, dodając to do gradle.propertiespliku:

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

Nie musi to być 2048M i 32g, więc zmień go tak, jak chcesz.

nieznany z nazwiska
źródło
2

Rozwiązany:
Wystarczy dodać
org.gradle.jvmargs=-Xmx1024m
w
gradle.properties
a jeśli nie istnieje, utwórz go.

reza_khalafi
źródło
0

Pracuję w Android Studio i napotkałem ten błąd podczas próby wygenerowania podpisanego pakietu APK do wydania. Byłem w stanie zbudować i przetestować APK debugowania bez problemu, ale jak tylko chciałem zbudować APK wersji, proces kompilacji będzie działał przez kilka minut, a następnie w końcu zakończy się komunikatem „Błąd java.lang.OutOfMemoryError: GC przekroczony limit napowietrzny ". Zwiększyłem rozmiary sterty zarówno dla maszyny wirtualnej, jak i kompilatora Android DEX, ale problem nadal występował. W końcu po wielu godzinach i kubkach kawy okazało się, że problem występuje w moim pliku „build.gradle” na poziomie aplikacji - miałem parametr „minifyEnabled” dla typu kompilacji wydania ustawiony na „false”, w konsekwencji uruchamiając rzeczy Proguard na temat kodu, który nie przeszedł procesu zmniejszania kodu (patrz https://developer.android.). Zmieniłem parametr „minifyEnabled” na „true”, a kompilacja wydania została wykonana jak sen :)

Krótko mówiąc, musiałem zmienić plik „build.gradle” na poziomie aplikacji z: // ...

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

//...

do

    //...

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

//...
Alex Ivan Howard
źródło
0

Aby zwiększyć rozmiar sterty w IntelliJ IDEA, postępuj zgodnie z następującymi instrukcjami. To zadziałało dla mnie.

Dla użytkowników systemu Windows

Przejdź do lokalizacji, w której jest zainstalowane IDE i wyszukaj następujące.

idea64.exe.vmoptions

Edytuj plik i dodaj następujące elementy.

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

To jest to !!

Dulith De Costa
źródło
0

możesz spróbować wprowadzić zmiany w ustawieniach serwera, odwołując się do tego obrazu i zwiększyć rozmiar pamięci do przetwarzania zmian procesu podświetlonych na żółto

możesz także wprowadzić zmiany w stosie Java, otwierając cmd-> set _java_opts -Xmx2g
2g (2 gigabajty) w zależności od złożoności programu

spróbuj użyć mniej stałych zmiennych i zmiennych temp

wprowadź opis zdjęcia tutaj

Rahul Jain
źródło
-1

Musisz zwiększyć rozmiar pamięci w Jdeveloper, przejdź do 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**
)

i

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**
Shashi
źródło
4
Te ustawienia są specyficzne tylko dla lokalnego IDE. To nie zadziała w środowisku Prod.
Feng
-1

W Netbeans pomocne może być zaprojektowanie maksymalnego rozmiaru sterty. Idź do Uruchom => Ustaw konfigurację projektu => Dostosuj . W oknie Uruchom wyskakującego okna przejdź do opcji VM , wypełnij -Xms2048m -Xmx2048m. Może rozwiązać problem z wielkością sterty.

Xiaogang
źródło
-1

Ponowne uruchomienie mojego MacBooka rozwiązało ten problem.

Tomasz
źródło