2012-11-20 28 views
18

Mam aplikację WWW Java działającą na serwerze Tomcat 7, która wydaje się mieć wyciek pamięci. Średnie wykorzystanie pamięci aplikacji wzrasta liniowo w miarę upływu czasu (pod wpływem JConsole). Po zużyciu pamięci osiąga plateau, wydajność spada znacznie. Czas reakcji wynosi od ~ 100ms do [300ms, 2500ms], co w rzeczywistości powoduje prawdziwe problemy.Przeciek pamięci w aplikacji WWW Java

profil pamięć JConsole mojej aplikacji: application memory profile

Korzystanie VisualVM, widzę, że co najmniej połowa pamięć jest używana przez tablic znakowych (tj char []), a większość (w przybliżeniu taka sama liczba każdego z 300 000 wystąpień) łańcuchów znaków są jednym z następujących: "Awaria alokacji", "Kopiuj", "koniec mniejszego GC", z których wszystkie wydają się być związane z powiadomieniem o usuwaniu śmieci. O ile mi wiadomo, aplikacja w ogóle nie monitoruje śmieciarek. VisualVM nie może znaleźć katalogu głównego GC dla żadnego z tych łańcuchów, więc trudno jest go śledzić.

Memory Analyzer zrzut stosu: heap dump, unreachable memory

Nie potrafię wyjaśnić, dlaczego płaskowyże zużycie pamięci tak, ale mam teorię, dlaczego wydajność degraduje raz robi. Jeśli pamięć jest pofragmentowana, aplikacja może zająć dużo czasu, aby przydzielić ciągły blok pamięci do obsługi nowych żądań.

Porównując to do wbudowanej aplikacji statusu serwera Tomcat, pamięć zwiększa się i zmniejsza poziomy na, ale nie trafia w wysoki "poziom", jak moja aplikacja. Nie ma również dużej liczby nieosiągalnych znaków [].

profil pamięć JConsole Tomcat aplikacji statusu serwera: enter image description here

Memory Analyzer zrzut stosu Tomcat status serwera applicationp: enter image description here

Gdzie mógłby te łańcuchy zostały przyznane i dlatego nie są one Czy zebrano śmieci? Czy są ustawienia Tomcat lub Java, które mogą mieć na to wpływ? Czy istnieją konkretne pakiety, które mogą mieć na to wpływ?

+0

W jaki sposób ustaliłeś, że te struny w szczególności nie są gc? – djechlin

+0

Gdy moja aplikacja jest bezczynna, wymuszam odśmiecanie i wykonuję zrzut sterty za pomocą VisualVM. Liczba tych ciągów zawsze rośnie z czasem i zawsze stanowi co najmniej połowę zużytej pamięci. –

+0

Analizator pamięci wskazuje, że nieosiągalne obiekty char [] i String odpowiadają za 176 MB sterty 210 MB. –

Odpowiedz

6

usunąłem następującą konfigurację JMX z tomcat\bin\setenv.bat:

set "JAVA_OPTS=%JAVA_OPTS% 
    -Dcom.sun.management.jmxremote=true 
    -Dcom.sun.management.jmxremote.port=9090 
    -Dcom.sun.management.jmxremote.ssl=false 
    -Dcom.sun.management.jmxremote.authenticate=false" 

nie mogę uzyskać szczegółowe pamięć zrzuca sterty, ale profil pamięci wygląda znacznie lepiej: enter image description here

24 godzin później, profil pamięć wygląda tak samo: enter image description here

3

Proponuję użyć pamięci MemoryAnalyzer do analizy sterty, daje znacznie więcej informacji.
http://www.eclipse.org/mat/ Istnieje samodzielna aplikacja i zaćma wbudowana. wystarczy uruchomić aplikację jmap na swojej aplikacji i przeanalizować wynik za pomocą tego.

+0

Analizator pamięci potwierdza, co wskazał VisualVM; nieosiągalne char [] i obiekty String stanowią około 176 MB sterty 210 MB. Niestety, nie pomaga to w wyjaśnieniu, skąd pochodzą strun i dlaczego nie są gromadzone śmieci. –

+0

Próbowałbym sprawdzić twoje osiągalne obiekty, a nie twoje nieosiągalne, domyślam się, że problem tam jest. – Zamir

0

Jako że brzmi to nieswoiście, jednym kandydatem byłby JSF. Ale wtedy też spodziewałbym się wycieku map hash.

Należy użyć JSF: W web.xml można spróbować:

  • javax.faces.STATE_SAVING_METHOD klient
  • com.sun.faces.numberOfViewsInSession
  • com.sun.faces.numberOfLogicalViews

Co do narzędzi: JavaMelody może być interesujące dla ciągłych statystyk, ale wymaga wysiłku.

2

Mogę polecić jvisualvm, który jest dołączany do każdej instalacji Java. Uruchom program, połącz się z Webapplication. Idź do Monitor -> Wysypisko na sterty. Może to zająć trochę czasu (w zależności od rozmiaru). Nawigacja przez zrzut sterty jest dość łatwa, ale znaczenie, jakie musisz wymyślić samemu sobie (niezbyt skomplikowane), np.

idź do Klasy (w heapdump), wybierz java.lang.String, kliknij prawym przyciskiem Pokaż W przypadkach Zobacz. Następnie zobaczysz w kolumnach z lewej strony String wystąpień aktywnych w systemie. Klick na jednej String instancji, a zobaczysz kilka String preferenes na prawym górnym części prawej tabeli, podobnie jak wartości z String.

Na dolnym prawym części prawej tabeli zobaczysz gdzie ta instancjaString jest odwoływać od. Tutaj musisz sprawdzić, skąd odwołuje się większość twoich * String * s. Ale w twoim przypadku (176/210, dobra możliwość znalezienia przykładów String, które wkrótce wywoła twoje problemy) powinno być jasne po pewnym sprawdzeniu, gdzie leży problem.

+0

Użyłem już JVisualVM do uzyskania zrzutu sterty, a narzędzie Memory Analyzer wskazuje, że ciągi, które są "przeciekające", są nieosiągalne (tj. Nic nie ma do nich odniesienia) i nie mogę się dowiedzieć, co je przydzielono. –

2

Wyrównywanie jest spowodowane dostępną pamięcią, która spada poniżej domyślnego progu procentowego, co powoduje uzyskanie pełnej wartości GC. To tłumaczy, dlaczego wydajność spada, gdy JVM ciągle zatrzymuje się, gdy próbuje znaleźć i zwolnić pamięć.

Zazwyczaj radzę przejrzeć pamięci podręczne obiektów, ale w twoim przypadku rozmiar twojego sterty jest po prostu zbyt niski dla instancji Tomcat i aplikacji internetowej. Zaleciłbym zwiększenie sterty do 1G (-Xms1024m -Xmx1024m), a następnie ponowne sprawdzenie użycia pamięci.

Jeśli nadal widzisz ten sam rodzaj zachowania, powinieneś zrobić kolejny zrzut sterty i spojrzeć na największych konsumentów po sznurku i Char. To moje doświadczenie to zazwyczaj mechanizmy buforujące. Albo zwiększ pamięć lub zmniejsz buforowanie, jeśli to możliwe. Niektóre pamięci podręczne definiują tylko liczbę obiektów, więc musisz zrozumieć, jak duży jest każdy buforowany obiekt.

Po zapoznaniu się z użytkowaniem pamięci można zmniejszyć ją ponownie, ale IMHO 512 MB będzie minimalne.

Aktualizacja:

Nie musisz martwić się o niedostępnych obiektów, jak powinny być czyszczone przez GC. Ponadto, jest normalne, że największymi konsumentami według typu są String i Char - większość obiektów będzie zawierać pewien rodzaj String, więc ma sens, że Strings i Chars są najbardziej rozpowszechnione według częstotliwości. Zrozumienie, co trzyma obiekty zawierające struny, jest kluczem do znalezienia konsumentów pamięci.

2

Po prostu napotkałem ten sam problem w zupełnie innej aplikacji, więc tomcat7 prawdopodobnie nie jest winien. Program Memory Analyzer pokazuje 10M nieosiągalnych instancji String w procesie (który działał przez około 2 miesiące), a większość/wszystkie z nich mają wartości odnoszące się do Garbage Collection (np. "Awaria alokacji", "koniec mniejszego GC")

Analyzer pamięci

Memory Analyzer

Pełna GC jest teraz uruchomiony co 2s ale te Struny nie dostać zebrane. Domyślam się, że trafiliśmy na błąd w kodzie GC. Używamy następującą wersję Java:

$ java -version 
java version "1.7.0_06" 
Java(TM) SE Runtime Environment (build 1.7.0_06-b24) 
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode) 

oraz następujące parametry VM:

-Xms256m -Xmx768m -server -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC 
-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:NewSize=32m -XX:MaxNewSize=64m 
-XX:SurvivorRatio=8 -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails 
-Xloggc:/path/to/file 
+0

Znalazłem tę notatkę w [dokumentacji programu Memory Analyzer] (http://wiki.eclipse.org/MemoryAnalyzer/FAQ), która wskazuje, że może to być "normalne" zachowanie GC zamiast błędu: "Podczas indeksu tworzenie, Memory Analyzer usuwa nieosiągalne obiekty, ponieważ różne algorytmy wyrzucania pamięci mają tendencję do pozostawiania pewnych śmieci za sobą (jeśli obiekt jest zbyt mały, przenoszenie i ponowne przypisywanie adresów jest drogie) .Jest to jednak nie więcej niż 3 4 procent. " –

1

Przez przypadek natknąłem się na następujących liniach w naszym pliku conf/catalina.properties Tomcat, które aktywują buforowanie String. Może to być związane z twoją sprawą, jeśli któryś z nich jest włączony. Wygląda na to, że do korzystania z tej funkcji potrzebne jest others are warning.

tomcat.util.buf.StringCache.byte.enabled=true 
#tomcat.util.buf.StringCache.char.enabled=true 
#tomcat.util.buf.StringCache.trainThreshold=500000 
#tomcat.util.buf.StringCache.cacheSize=5000 
0

spróbuje użyć matę i upewnić się, że podczas analizowania heapdump, czy nie wychodzenie z niedostępnych obiektów.

Aby to zrobić, wykonaj samouczek: here.

Następnie można uruchomić prosty Mem Leak analizy (This jest dobry tutorial)

To powinno szybko doprowadzi Cię do przyczyn.

Powiązane problemy