Jak radzić sobie z błędem „java.lang.OutOfMemoryError: Java heap space”?


416

Piszę aplikację Swing po stronie klienta (graficzny projektant czcionek) na Javie 5 . Ostatnio java.lang.OutOfMemoryError: Java heap spacepopełniam błąd, ponieważ nie jestem konserwatywny w kwestii użycia pamięci. Użytkownik może otworzyć nieograniczoną liczbę plików, a program przechowuje otwarte obiekty w pamięci. Po szybkich badaniach znalazłem ergonomię w wirtualnej maszynie Java 5.0 i innych, którzy mówili na maszynie Windows, że domyślna maksymalna wielkość sterty maszyny JVM to 64MB.

W tej sytuacji, jak mam sobie poradzić z tym ograniczeniem?

Mógłbym zwiększyć maksymalny rozmiar sterty za pomocą opcji wiersza poleceń do java, ale wymagałoby to wykrycia dostępnej pamięci RAM i napisania programu uruchamiającego lub skryptu. Poza tym zwiększenie do pewnego skończonego maksimum ostatecznie nie pozbywa się problemu.

Mógłbym przepisać część mojego kodu, aby często utrwalać obiekty w systemie plików (korzystanie z bazy danych to samo) w celu zwolnienia pamięci. To może działać, ale prawdopodobnie też dużo pracy.

Jeśli mógłbyś wskazać mi szczegóły powyższych pomysłów lub niektóre alternatywy, takie jak automatyczna pamięć wirtualna, dynamiczne zwiększanie wielkości sterty , byłoby świetnie.


Domyślny maksymalny rozmiar sterty 64 MB to sprzed J2SE 5.0. Aby uzyskać informacje o J2SE 8.0, zobacz „Ergonomia Garbage Collector” na docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Andy Thomas

Jeśli wylądowałeś tutaj, ponieważ każde pytanie OOM jest duplikowane na to, sprawdź również: stackoverflow.com/questions/299659/… Zapewnia rozwiązanie do czyszczenia referencji pamięci „na czas” przed OOM. SoftReferences mogą być narzędziem, które rozwiązuje twój rzeczywisty problem.
Steve Steiner

Odpowiedzi:


244

Ostatecznie zawsze masz skończoną maksymalną stertę, z której możesz korzystać bez względu na platformę, na której pracujesz. W 32-bitowym systemie Windows jest to około 2GB(nie sterty, ale całkowita ilość pamięci na proces). Zdarza się, że Java decyduje się na zmniejszenie domyślnej wartości (prawdopodobnie po to, aby programiści nie mogli tworzyć programów, które mają przydział pamięci, nie wpadając w ten problem i nie musząc dokładnie zbadać, co robią).

Biorąc to pod uwagę, istnieje kilka metod, które można zastosować, aby określić, jakiej ilości pamięci potrzebujesz lub zmniejszyć ilość używanej pamięci. Jednym z powszechnych błędów w przypadku śmieci, takich jak Java lub C #, jest przechowywanie odniesień do obiektów, których już nie używasz, lub przydzielanie wielu obiektów, gdy można zamiast tego użyć ich ponownie . Tak długo, jak obiekty mają do nich odniesienie, będą nadal korzystać z miejsca na sterty, ponieważ śmieciarz ich nie usunie.

W takim przypadku możesz użyć profilera pamięci Java, aby ustalić, które metody w twoim programie przydzielają dużą liczbę obiektów, a następnie ustalić, czy istnieje sposób, aby upewnić się, że nie są one już przywoływane, lub w ogóle ich nie przydzielać. Jedną z opcji, z których korzystałem w przeszłości, jest „JMP” http://www.khelekore.org/jmp/ .

Jeśli stwierdzisz, że alokujesz te obiekty z jakiegoś powodu i musisz trzymać się referencji (w zależności od tego, co robisz, może tak być), po prostu musisz zwiększyć maksymalny rozmiar sterty podczas uruchamiania programu. Jednak po wykonaniu profilowania pamięci i zrozumieniu, w jaki sposób przydzielane są twoje obiekty, powinieneś mieć lepszy pomysł na to, ile pamięci potrzebujesz.

Ogólnie rzecz biorąc, jeśli nie możesz zagwarantować, że twój program będzie działał w pewnej skończonej ilości pamięci (być może w zależności od wielkości wejściowej), zawsze napotkasz ten problem. Dopiero po wyczerpaniu tego wszystkiego będziesz musiał zajrzeć do buforowania obiektów na dysk itp. W tym momencie powinieneś mieć bardzo dobry powód, aby powiedzieć „Potrzebuję Xgb pamięci” do czegoś i nie możesz obejść tego, ulepszając algorytmy lub wzorce alokacji pamięci. Zasadniczo dzieje się tak zwykle tylko w przypadku algorytmów działających na dużych zestawach danych (takich jak baza danych lub jakiś program do analizy naukowej), a następnie techniki takie jak buforowanie i IO odwzorowywane w pamięci stają się przydatne.


6
OpenJDK i OracleJDK mają wbudowany profiler - jvisualvm. Jeśli chcesz więcej udogodnień, polecam komercyjny Yourkit.
Petr Gladkikh


85

Możesz określić dla każdego projektu, ile miejsca na stosie chce twój projekt

Poniżej znajduje się dla Eclipse Helios / Juno / Kepler :

Kliknij prawym przyciskiem myszy

 Run As - Run Configuration - Arguments - Vm Arguments, 

następnie dodaj to

-Xmx2048m

1
Cześć bighostkim i cuongHuyTo, gdzie jest „Argumenty” .. Mogę zobaczyć Upto Uruchom konfigurację. Proszę o telefon. Potrzebuję pobrać i przechowywać prawie 2000 kontaktów z Gmaila. Awaria z powodu wyjątku
braku

@AndroiRaji: kliknij prawym przyciskiem myszy na klasę Java, która ma uruchamialny main (czyli „public static void main (String [] args)”), a następnie wybierz Run As - Run Configuration. Następnie „Argumenty” to zakładka zaraz po Main (widoczne są zakładki Main, Arguments, JRE, Classpath, Source, Environment, Common).
CuongHuyTo

47

Zwiększenie wielkości stosu nie jest „naprawą”, lecz „tynkiem”, w 100% tymczasowym. Zepsuje się w innym miejscu. Aby uniknąć tych problemów, napisz kod o wysokiej wydajności.

  1. W miarę możliwości używaj zmiennych lokalnych.
  2. Upewnij się, że wybierasz prawidłowy obiekt (EX: Wybór między String, StringBuffer i StringBuilder)
  3. Użyj dobrego systemu kodu dla swojego programu (EX: Używanie zmiennych statycznych VS zmiennych niestatycznych)
  4. Inne rzeczy, które mogą działać na twoim kodzie.
  5. Staraj się poruszać z potężnym GWINTEM

To prawda. Próbuję naprawić jeden problem, w którym otrzymuję OOM w wątku AWT, ale jeśli użyję innego nowego wątku, nie dostanę problemu OOM. Wszystko, co mogę znaleźć w Internecie, to zwiększenie wielkości sterty dla wątku AWT.
Ashish

@ Ash: Tak, napraw podstawowy problem zamiast szukać tynków.
Sok z cytryny

Odśmiecanie pamięci i podejście do zarządzania pamięcią w Javie miało rozwiązać wszystkie te komplikacje malloc-dealloc swoich poprzedników :( Oczywiście całkowicie się zgadzam z tą odpowiedzią, szkoda tylko, że domyślne ustawienia nie ułatwiają pisania kodu przy pomocy lean data -struktury, które są czyszczone JAK NAJSZYBCIEJ
Davos,

31

Duże zastrzeżenie - w moim biurze stwierdziliśmy, że (na niektórych komputerach z systemem Windows) nie możemy przeznaczyć więcej niż 512 m na stertę Java. Okazało się, że jest to spowodowane programem antywirusowym Kaspersky zainstalowanym na niektórych z tych komputerów. Po odinstalowaniu tego produktu AV, stwierdziliśmy, że możemy przydzielić co najmniej 1,6 GB, tj. -Xmx1600m(M jest obowiązkowe, w przeciwnym razie spowoduje kolejny błąd „Zbyt mała początkowa sterty”).

Nie mam pojęcia, czy dzieje się tak w przypadku innych produktów AV, ale prawdopodobnie dzieje się tak, ponieważ program AV rezerwuje mały blok pamięci w każdej przestrzeni adresowej, zapobiegając w ten sposób pojedynczemu naprawdę dużemu przydziałowi.


22

Argumenty VM działały dla mnie w zaćmieniu. Jeśli używasz Eclipse w wersji 3.4, wykonaj następujące czynności

przejdź do, Run --> Run Configurations -->a następnie wybierz projekt w maven build -> następnie wybierz zakładkę „JRE” -> następnie naciśnij -Xmx1024m.

Możesz też Run --> Run Configurations --> select the "JRE" tab -->wpisać -Xmx1024m

Powinno to zwiększyć stos pamięci dla wszystkich kompilacji / projektów. Powyższy rozmiar pamięci wynosi 1 GB. Możesz zoptymalizować, jak chcesz.


18

Tak, dzięki -Xmxczemu możesz skonfigurować więcej pamięci dla JVM. Aby mieć pewność, że nie wyciekasz ani nie marnujesz pamięci. Zrób zrzut stosu i użyj Eclipse Memory Analyzer do analizy zużycia pamięci.


JVMJ9VM007E Nierozpoznana opcja wiersza polecenia: -Xmx Nie można utworzyć wirtualnej maszyny Java. Downvote
Philip Rego

17

Chciałbym dodać rekomendacje z artykułu dotyczącego rozwiązywania problemów z wyrocznią .

Wyjątek w wątku nazwa_wątku: java.lang.OutOfMemoryError: Przestrzeń sterty Java

Szczegółowy komunikat Przestrzeń sterty Java wskazuje, że obiekt nie mógł zostać przydzielony w sterty Java. Ten błąd niekoniecznie oznacza wyciek pamięci

Możliwe przyczyny:

  1. Prosty problem z konfiguracją , gdy określony rozmiar sterty jest niewystarczający dla aplikacji.

  2. Aplikacja mimowolnie przechowuje odwołania do obiektów , co zapobiega gromadzeniu śmieci.

  3. Nadmierne użycie finalizatorów .

Kolejne potencjalne źródło tego błędu powstaje w przypadku aplikacji, które nadmiernie wykorzystują finalizatory. Jeśli klasa ma metodę finalizacji, wówczas obiektom tego typu nie odzyskuje się miejsca podczas odśmiecania

Po odśmiecaniu obiekty są kolejkowane w celu finalizacji , co nastąpi później. finalizatory są wykonywane przez wątek demona obsługujący kolejkę finalizacji. Jeśli wątek finalizatora nie nadąża za kolejką finalizacji, sterty Java mogą się zapełnić i tego typu zgłoszony wyjątek OutOfMemoryError .

Jednym ze scenariuszy, który może powodować taką sytuację, jest sytuacja, w której aplikacja tworzy wątki o wysokim priorytecie, które powodują, że kolejka finalizacji rośnie w tempie szybszym niż tempo, w jakim wątek finalizatora obsługuje tę kolejkę.


9

Wykonaj poniższe kroki:

  1. Otwórz catalina.shz tomcat / bin.

  2. Zmień JAVA_OPTS na

    JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m 
    -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m 
    -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
  3. Uruchom ponownie kocura


8

Przeczytałem gdzieś indziej - catch java.lang.OutOfMemoryError i w bloku catch możesz zwolnić wszystkie zasoby, o których wiesz, że mogą zużywać dużo pamięci, zamknąć połączenia i tak dalej, a następnie wykonaj System.gc() następnie ponownie cokolwiek zamierzałeś to zrobić.

Innym sposobem jest to, chociaż nie wiem, czy to zadziała, ale obecnie testuję, czy zadziała w mojej aplikacji.

Chodzi o to, aby wykonać czyszczenie pamięci przez wywołanie System.gc (), o której wiadomo, że zwiększa wolną pamięć. Możesz to nadal sprawdzać po wykonaniu kodu pożerającego pamięć.

//Mimimum acceptable free memory you think your app needs
long minRunningMemory = (1024*1024);

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();

6
Ogólnie rzecz biorąc, myślę, że JVM woli raczej Garbage Collect (GC) niż wyrzucać OutOfMemoryError. Jawne wywołanie System.gc () po OutOfMemoryError może prawdopodobnie pomóc w niektórych maszynach wirtualnych / konfiguracjach, ale nie spodziewałbym się, że będzie działać bardzo dobrze w ogólnym przypadku. Jednak usunięcie niepotrzebnych odniesień do obiektów na pewno pomogłoby w prawie wszystkich przypadkach.
Mike Clark,

6
@mwangi Wywołanie System.gc () bezpośrednio z kodu jest ogólnie złym pomysłem. To tylko sugestia dla JVM, że GC powinno być wykonane, ale nie ma absolutnie żadnej gwarancji , że zostanie wykonane.

7

Prostym sposobem rozwiązania OutOfMemoryErrorw Javie jest zwiększenie maksymalnego rozmiaru sterty za pomocą opcji JVM-Xmx512M , co natychmiast rozwiąże błąd OutOfMemoryError. Jest to moje preferowane rozwiązanie, gdy otrzymuję OutOfMemoryError w Eclipse, Maven lub ANT podczas budowania projektu, ponieważ w oparciu o rozmiar projektu można łatwo zabraknąć pamięci.

Oto przykład zwiększania maksymalnego rozmiaru sterty JVM, a także lepiej zachować stosunek -Xmx do -Xms 1: 1 lub 1: 1,5, jeśli ustawiasz rozmiar sterty w aplikacji Java.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Link referencyjny


1
Masz pojęcie, dlaczego musimy zachować je w stosunku 1: 1 lub 1: 1,5?
ernesto

7

Domyślnie do programowania JVM używa małego rozmiaru i małej konfiguracji dla innych funkcji związanych z wydajnością. Ale w przypadku produkcji można dostroić np. (Dodatkowo może istnieć konfiguracja specyficzna dla serwera aplikacji) -> (Jeśli nadal nie ma wystarczającej ilości pamięci do zaspokojenia żądania i sterty osiągnęły już maksymalny rozmiar, wystąpi błąd OutOfMemoryError)

-Xms<size>        set initial Java heap size
-Xmx<size>        set maximum Java heap size
-Xss<size>        set java thread stack size

-XX:ParallelGCThreads=8
-XX:+CMSClassUnloadingEnabled
-XX:InitiatingHeapOccupancyPercent=70
-XX:+UnlockDiagnosticVMOptions
-XX:+UseConcMarkSweepGC
-Xms512m
-Xmx8192m
-XX:MaxPermSize=256m (in java 8 optional)

Na przykład: Na platformie Linux dla trybu produkcyjnego preferowane ustawienia.

Po pobraniu i skonfigurowaniu serwera w ten sposób http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1. utwórz plik setenv.sh w folderze / opt / tomcat / bin /

   touch /opt/tomcat/bin/setenv.sh

2. Otwórz i zapisz te parametry, aby ustawić preferowany tryb.

nano  /opt/tomcat/bin/setenv.sh 

export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+CMSClassUnloadingEnabled"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=70"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockDiagnosticVMOptions"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseConcMarkSweepGC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=256M"

3)service tomcat restart

Pamiętaj, że JVM zużywa więcej pamięci niż tylko stertę. Na przykład metody Java, stosy wątków i uchwyty rodzime są przydzielane w pamięci oddzielnie od sterty, a także wewnętrzne struktury danych JVM.


7

Napotkałem ten sam problem z wielkością sterty Java.

Mam dwa rozwiązania, jeśli używasz java 5 (1.5).

  1. po prostu zainstaluj jdk1.6 i przejdź do preferencji eclipse i ustaw ścieżkę jre w wersji jav1 1.6 po zainstalowaniu.

  2. Sprawdź swój argument maszyny wirtualnej i niech będzie, czymkolwiek jest. wystarczy dodać jeden wiersz poniżej wszystkich argumentów obecnych w argumentach VM jako -Xms512m -Xmx512m -XX: MaxPermSize = ... m (192m).

Myślę, że to zadziała ...


7

Jeśli musisz monitorować zużycie pamięci w czasie wykonywania, java.lang.managementoferty pakietowe, MBeansktóre można wykorzystać do monitorowania pul pamięci w maszynie Wirtualnej (np. Przestrzeń eden, generowanie w ten sposób itp.), A także zachowanie usuwania śmieci.

Ilość wolnego miejsca na stosie zgłaszana przez te komponenty MBean będzie się znacznie różnić w zależności od zachowania GC, szczególnie jeśli twoja aplikacja generuje wiele obiektów, które są później GC-ed. Jednym z możliwych podejść jest monitorowanie wolnej przestrzeni sterty po każdym pełnym GC, którego możesz użyć do podjęcia decyzji o zwolnieniu pamięci przez utrwalenie obiektów.

Ostatecznie najlepszym rozwiązaniem jest ograniczenie w jak największym stopniu przechowywania pamięci, a wydajność pozostaje akceptowalna. Jak wspomniano w poprzednim komentarzu, pamięć jest zawsze ograniczona, ale Twoja aplikacja powinna mieć strategię radzenia sobie z wyczerpaniem pamięci.


5

Zauważ, że jeśli potrzebujesz tego w sytuacji wdrażania, rozważ użycie Java WebStart (z wersją „ondisk”, a nie sieciową - możliwe w Javie 6u10 i nowszych), ponieważ pozwala ona na podanie różnych argumentów dla JVM w sposób krzyżowy sposób platformy.

W przeciwnym razie będziesz potrzebować programu uruchamiającego specyficznego dla systemu operacyjnego, który ustawia potrzebne argumenty.


Java WebStart jest wycofywany. Nie znam jeszcze odpowiedniego zamiennika.
Thorbjørn Ravn Andersen

1

Jeśli ten problem występuje w Wildfly 8 i JDK1.8, musimy określić ustawienia MaxMetaSpace zamiast ustawień PermGen.

Na przykład musimy dodać poniższą konfigurację w pliku setenv.sh wildfly. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Aby uzyskać więcej informacji, sprawdź problem z kupą Wildfly


1

Jeśli chodzi o netbeans, możesz ustawić maksymalny rozmiar sterty, aby rozwiązać problem.

Przejdź do „Uruchom”, a następnie -> „Ustaw konfigurację projektu” -> „Dostosuj” -> „Uruchom” wyskakującego okna -> „Opcja VM” -> wypełnij „-Xms2048m -Xmx2048m” .


1

Kontynuując przydzielanie i przechowywanie odniesień do obiektu, zapełnisz dowolną ilość pamięci.

Jedną z opcji jest wykonanie przezroczystego zamykania i otwierania pliku, gdy zmieniają zakładki (przytrzymujesz tylko wskaźnik do pliku, a gdy użytkownik przełącza zakładkę, zamykasz i czyścisz wszystkie obiekty ... spowoduje to wolniejszą zmianę pliku ... ale ...) i być może przechowuj tylko 3 lub 4 pliki w pamięci.

Inną rzeczą, którą powinieneś zrobić, jest to, że kiedy użytkownik otwiera plik, ładuje go i przechwytuje dowolny OutOfMemoryError, a następnie (ponieważ nie można go otworzyć) zamknąć ten plik, wyczyścić jego obiekty i ostrzec użytkownika, że ​​powinien zamknąć nieużywany akta.

Twój pomysł na dynamiczne rozszerzanie pamięci wirtualnej nie rozwiązuje problemu, ponieważ maszyna ma ograniczone zasoby, więc powinieneś być ostrożny i zajmować się problemami z pamięcią (a przynajmniej bądź ostrożny z nimi).

Kilka wskazówek, które widziałem podczas wycieków pamięci to:

-> Pamiętaj, że jeśli włożysz coś do kolekcji, a następnie zapomnisz o tym, nadal będziesz mieć silne odniesienie do tego, więc zlikwiduj kolekcję, wyczyść ją lub zrób coś z tym ... jeśli nie, znajdziesz wyciek pamięci trudny do znalezienia.

-> Być może użycie kolekcji ze słabymi referencjami (słaba mapa ...) może pomóc w problemach z pamięcią, ale musisz być ostrożny, ponieważ może się okazać, że obiekt, którego szukasz, został zebrany.

-> Innym pomysłem, jaki znalazłem, jest opracowanie trwałej kolekcji, która przechowywana jest na obiektach bazy danych, które są najmniej używane i ładowane w sposób przezroczysty. To prawdopodobnie najlepsze podejście ...


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.