Jest to problem, który od kilku miesięcy próbuję znaleźć. Mam uruchomioną aplikację Java, która przetwarza źródła XML i zapisuje wynik w bazie danych. Występowały sporadyczne problemy z zasobami, które są bardzo trudne do wyśledzenia.
Tło: Na pudełku produkcyjnym (gdzie problem jest najbardziej zauważalny) nie mam szczególnie dobrego dostępu do pudełka i nie mogę uruchomić Jprofiler. To pudełko to 64-bitowa czterordzeniowa maszyna o pojemności 8 GB z systemem Centos 5.2, tomcat6 i java 1.6.0.11. Zaczyna się od tych java-opts
JAVA_OPTS="-server -Xmx5g -Xms4g -Xss256k -XX:MaxPermSize=256m -XX:+PrintGCDetails -
XX:+PrintGCTimeStamps -XX:+UseConcMarkSweepGC -XX:+PrintTenuringDistribution -XX:+UseParNewGC"
Stos technologii jest następujący:
- Centos 64-bitowy 5.2.0
- Java 6u11
- Tomcat 6
- Wiosna / WebMVC 2.5
- Hibernacja 3
- Kwarc 1.6.1
- DBCP 1.2.1
- MySQL 5.0.45
- Ehcache 1.5.0
- (i oczywiście wiele innych zależności, w szczególności biblioteki dżakarta-commons)
Najbliżej odtworzenia problemu jest 32-bitowa maszyna z mniejszymi wymaganiami dotyczącymi pamięci. Nad czym mam kontrolę. Sondowałem to na śmierć za pomocą JProfilera i naprawiłem wiele problemów z wydajnością (problemy z synchronizacją, prekompilacją / buforowaniem zapytań xpath, zmniejszeniem puli wątków i usunięciem niepotrzebnego pobierania wstępnego hibernacji i nadgorliwego „podgrzewania pamięci podręcznej” podczas przetwarzania).
W każdym przypadku program profilujący wykazał, że pochłaniają one ogromne ilości zasobów z tego czy innego powodu i że po wprowadzeniu zmian nie były to już podstawowe zasoby.
Problem: maszyna JVM wydaje się całkowicie ignorować ustawienia użycia pamięci, wypełnia całą pamięć i przestaje odpowiadać. Jest to problem dla klienta stojącego przed końcem, który oczekuje regularnej ankiety (co 5 minut i 1 minuta ponowienia), a także dla naszych zespołów operacyjnych, które są stale powiadamiane, że skrzynka przestała odpowiadać i muszą ją ponownie uruchomić. Na tym pudełku nie ma nic znaczącego.
Wygląda na to, że problem dotyczy czyszczenia pamięci. Używamy modułu zbierającego ConcurrentMarkSweep (jak wspomniano powyżej), ponieważ oryginalny moduł zbierający STW powodował przekroczenia limitów czasu JDBC i stawał się coraz wolniejszy. Dzienniki pokazują, że wraz ze wzrostem użycia pamięci zaczyna rzucać awarie cms i powraca do oryginalnego kolektora stop-the-world, który wydaje się nie gromadzić prawidłowo.
Jednak działając z jprofilerem, przycisk "Uruchom GC" wydaje się ładnie czyścić pamięć, zamiast pokazywać rosnący ślad, ale ponieważ nie mogę podłączyć jprofilera bezpośrednio do skrzynki produkcyjnej, a rozwiązywanie sprawdzonych hotspotów wydaje się nie działać. po lewej stronie z voodoo strojenia Garbage Collection w ciemno.
Co próbowałem:
- Profilowanie i naprawianie hotspotów.
- Używanie garbage collectorów STW, Parallel i CMS.
- Praca z minimalnymi / maksymalnymi rozmiarami sterty w krokach 1 / 2,2 / 4,4 / 5,6 / 6.
- Działa z przestrzenią permgen w przyrostach 256 MB do 1 Gb.
- Wiele kombinacji powyższych.
- Skonsultowałem się również z JVM [odniesienie do strojenia] (http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html), ale tak naprawdę nie mogę znaleźć nic wyjaśniającego to zachowanie ani żadnych przykładów _which_ tuning parametry do użycia w takiej sytuacji.
- Próbowałem również (bez powodzenia) jprofilera w trybie offline, łącząc się z jconsole, visualvm, ale nie mogę znaleźć niczego, co zinterpretuje moje dane dziennika gc.
Niestety problem też pojawia się sporadycznie, wydaje się być nieprzewidywalny, może trwać dni lub nawet tydzień bez żadnych problemów, albo może zawieść 40 razy w ciągu dnia, a jedyne, co zdaje się łapać konsekwentnie, to to wyrzucanie śmieci działa.
Czy ktoś może doradzić, jak:
a) Dlaczego JVM używa 8 fizycznych gigów i 2 GB przestrzeni wymiany, kiedy jest skonfigurowana tak, aby maksymalnie wynosić mniej niż 6.
b) Odniesienie do strojenia GC, które faktycznie wyjaśnia lub podaje rozsądne przykłady kiedy i jakiego rodzaju ustawienia używać zaawansowanych kolekcji.
c) Odniesienie do najczęstszych wycieków pamięci w Javie (rozumiem odwołania nieodebrane, ale mam na myśli poziom biblioteki / frameworka lub coś bardziej nieodłącznego w strukturach danych, takich jak hashmapy).
Dziękuję za wszelkie uwagi, których możesz udzielić.
EDYTUJ
Emil H:
1) Tak, mój klaster programistyczny jest lustrem danych produkcyjnych, aż do serwera multimediów. Podstawową różnicą jest 32/64-bitowy i ilość dostępnej pamięci RAM, której nie mogę łatwo odtworzyć, ale kod, zapytania i ustawienia są identyczne.
2) Istnieje jakiś starszy kod, który opiera się na JaxB, ale podczas zmiany kolejności zadań, aby uniknąć konfliktów planowania, generalnie wyeliminowałem to wykonanie, ponieważ jest ono uruchamiane raz dziennie. Podstawowy parser używa zapytań XPath, które wywołują pakiet java.xml.xpath. To było źródło kilku punktów aktywnych, ponieważ jeden z zapytań nie był wstępnie kompilowany, a dwa odniesienia do nich były zapisane na sztywno. Utworzyłem pamięć podręczną z ochroną wątków (hashmap) i rozważyłem odwołania do zapytań xpath, aby były ostatecznymi statycznymi ciągami znaków, co znacznie zmniejszyło zużycie zasobów. Zapytanie nadal stanowi dużą część przetwarzania, ale powinno tak być, ponieważ jest to główna odpowiedzialność aplikacji.
3) Dodatkowa uwaga, drugim głównym konsumentem są operacje na obrazach z JAI (ponowne przetwarzanie obrazów z kanału). Nie znam bibliotek graficznych Java, ale z tego, co odkryłem, nie są one szczególnie nieszczelne.
(dzięki za dotychczasowe odpowiedzi, ludzie!)
AKTUALIZACJA:
Mogłem połączyć się z instancją produkcyjną za pomocą VisualVM, ale wyłączyłem opcję wizualizacji GC / run-GC (chociaż mogłem to wyświetlić lokalnie). Interesująca rzecz: alokacja sterty maszyny wirtualnej jest zgodna z JAVA_OPTS, a rzeczywista przydzielona sterta mieści się wygodnie przy 1-1,5 giga i nie wydaje się przeciekać, ale monitorowanie na poziomie skrzynki nadal pokazuje wzór wycieku, ale tak jest nie ma odzwierciedlenia w monitorowaniu maszyny wirtualnej. Na tym pudełku nie ma nic innego, więc jestem zaskoczony.