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ć?


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:


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 :)


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ń.


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


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


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


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


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

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

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

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ś.


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.


2

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


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

//...

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


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


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

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.


-1

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


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.