2012-03-09 14 views
5

Opracowałem aplikację internetową, która przetwarza ogromną ilość danych i zajmuje dużo czasu?
Garbage Collector pobiera zbyt dużo czasu procesora

Teraz robię profilowanie mojej aplikacji i zauważyłem jedną bardzo złą rzecz na temat GC.
Po osiągnięciu pełnej wartości GC zatrzymuje cały proces na 30 - 40 sekund.

Zastanawiam się, czy jest jakiś sposób, aby to poprawić. Nie chcę talia mojego procesora tyle czasu tylko w GC. Poniżej znajdują się szczegóły, które mogą być użyteczne:

  1. używam Java 1.6.0.23
  2. moja aplikacja zajmuje pamięć 20 GB max.
  3. Pełny GC występuje po każdych 14 minutach.
  4. pamięci przed GC 20 GB po GC wynosi 7,8 PL
  5. Użycie pamięci CPU (to pokazano w menadżera zadań) wynosi 41 GB.
  6. Po zakończeniu procesu (JVM nadal działa) Używana pamięć 5 GB i wolna pamięć 15 GB.
+2

* Określona * JVM, która jest używana i opcje pamięci, jeśli występują, muszą być opatrzone adnotacją w pytaniu. Uwzględnij także wszelkie wyniki profilowania i sposób ich uzyskania. –

+0

Możesz okresowo sugerować, aby GC działał w strategicznie wybranych punktach w czasie, aby uzyskać więcej kolekcji, które trwają krócej. –

+0

@pst Dodałem większość szczegółów, które zaobserwowałem. –

Odpowiedz

3

Im mniej rzeczy, które new, tym mniej rzeczy należy zebrać.

Załóżmy, że masz klasę A. można umieścić w nim odwołanie do innej instancji klasy A. ten sposób można zrobić „darmową listę” wystąpień A. Kiedykolwiek potrzebujesz A, tylko pop jeden z darmowej listy. Jeśli wolna lista jest pusta, wtedy new jeden.

Gdy już go nie potrzebujesz, naciśnij go na wolną listę.

To może zaoszczędzić wiele czasu.

+2

Przeczytałem, że "Łączenie obiektów" może być wzorcem zapobiegającym we wszystkich przypadkach, w których budowa obiektu jest kosztowna, patrz tutaj: http://www.ibm.com/developerworks/java/library/j-jtp01274 /index.html pod "Object Pooling" – weston

+0

Pula Apache Commons, zapewnia prostą strukturę do łączenia obiektów, ale jeśli obiekty są obiektami prostymi (które nie są drogie w budowie), nie ma zysku. Nawiasem mówiąc, jeśli tworzony wykres obiektów zawiera wiele obiektów, a struktura wykresów jest zawsze taka sama, być może mogłoby to pomóc. –

+0

@weston: Robię to tylko po ręcznym próbkowaniu stosu dowodzi alokacji i dealokacji zajmuje znaczny procent czasu. Ponadto, ludzie piszą, co czują się pisząc, więc jeśli ktoś mówi, że coś jest anty-patttern, co nie znaczy, że jest. Dobrze jest zarezerwować własną ocenę. –

4

Jeśli używasz 64 bitowej architekturze dodają:

-XX:+UseCompressedOops adresy 64-bitowe są przekształcane na 32-bitowy

Używaj G1GC zamiast CMS:

-XX:+UseG1GC - używać przyrostowe kroki

Set ten sam początkowy i maksymalny rozmiar: -Xms5g -Xmx5g

parametry Tune (tylko przykład):

-XX:MaxGCPauseMillis=100 -XX:GCPauseIntervalMillis=1000

Zobacz Java HotSpot VM Optionswydajności Opcje

4

Albo poprawić aplikację poprzez ponowne wykorzystanie zasobów lub kick-w System.gc() się w pewnych krytycznych obszarach aplikacji (co nie jest na pewno ci pomoże). Najprawdopodobniej masz gdzieś wyciek pamięci, który musisz zbadać iw konsekwencji zrestrukturyzować kod.

+2

Samo wywoływanie System.gc() jest zawsze złym pomysłem. Jeśli polegasz na tym wezwaniu ze względu na wydajność, jest to dobry wskaźnik, że coś jest nie tak z kodem. Ponadto nie można nic zrobić, to jest tylko żądanie, które JVM może całkowicie zignorować. – Black

3

Czas spędzony na GC zależy od dwóch czynników:

  • Ile obiekty są na żywo (= można dojechać od nikogo)
  • Ile martwe przedmioty wdrożyć finalize()

Obiekty, których nie można uzyskać i które nie używają finalize(), nie kosztują niczego w Javie, dlatego Java jest zwykle na równi z innymi językami, takimi jak C++ (i często znacznie lepiej, ponieważ C++ poświęca dużo czasu na usuwanie obiektów) .

Więc co trzeba zrobić w aplikacji jest zmniejszyć liczbę obiektów, które przetrwały i/lub wyciętych referencje do obiektów (które nie są już potrzebne) wcześniej w kodzie. Przykład:

Gdy masz bardzo długi sposób, będzie można utrzymać przy życiu wszystkie obiekty, które można odwołać ze zmiennych lokalnych. Jeśli podzielisz tę metodę na wiele mniejszych metod, odniesienia zostaną utracone szybciej, a GC nie będzie musiał zajmować się tymi obiektami.

Jeśli umieścisz wszystko, czego możesz potrzebować w ogromnych mapach hash, mapy zachowa wszystkie te przypadki żyje aż Twój kod zakończy. Więc nawet jeśli już ich nie potrzebujesz, GC będzie musiał spędzać na nich czas.

+0

Dzięki Aaron, które wskazuje, że tutaj powiedziałeś może być problem w mojej aplikacji. Ponieważ istnieje kilka ogromnych metod i używam wielu map i list do danych w pamięci podręcznej. Ale muszę buforować dane, ponieważ bardzo często je stosowałem. –

+0

Więc czasy GC są coraz gorsze, im więcej przedmiotów żyjesz, nawet jeśli nikt nie umiera? – weston

+0

@weston: Cóż, na żywo!= martwy. Martwe obiekty nie są liczone, chyba że mają metodę "finalize()" (którą GC musi wywołać, zanim może o niej zapomnieć) lub żyły przez długi czas (i zostały przeniesione poza przestrzeń eden). –

5

Istnieje wiele algorytmów używanych przez współczesne maszyny JVM do usuwania śmieci. Niektóre algorytmy, takie jak liczenie odwołań, są tak szybkie, a niektóre, takie jak kopiowanie pamięci, są tak wolne. Możesz zmienić swój kod, aby pomóc JVM w korzystaniu z szybszych algorytmów przez większość czasu.

Jednym z najszybszych algorytmów jest liczenie odwołań, a jak sama nazwa wskazuje, zlicza odniesienia do obiektu, a gdy osiągnie zero, jest gotowy do zbierania śmieci, a następnie zmniejsza liczbę odwołań do obiektów przywoływanych przez bieżący obiekt GCed.

Aby JVM użyciu algorytmu, uniknąć odniesienia koliste (odniesienia obiektowi B, następnie B Referencje Referencje C C D ...., Y odwołuje się ponownie). Ponieważ nawet gdy cały wykres obiektu nie jest osiągalny, żaden z liczników odniesienia obiektu nie osiąga zera.

Mogłabyś dopiero przełamać krąg, kiedy nie trzeba obiekty w kręgu nic więcej (poprzez przypisanie wartości null do jednego z referencjami) ....

+1

Choć prawdą jest, że istnieje wiele mechanizmów GC, a liczenie odwołań jest jednym z nich, już dawno zostało zepchnięte na dalszy plan. Obecna maszyna JVM używa kilku różnych technik do ominięcia rodzaju identyfikowanych cykli odniesienia. Pierwszy i najprostszy to "kopia na żywo". To nie wygląda na wszystkie przydzielone obiekty. Zamiast tego zaczyna się od powiązanych obiektów (korzeni) wątku, a następnie zaczyna kopiować wszystkie obiekty dostępne do nowej przestrzeni. A po zakończeniu, po prostu anuluje wszystko, co zostało. Tak więc, wszelkie cykle odniesienia, które nie mają ścieżki od korzenia nitki, znikają. – chaotic3quilibrium

+0

Wiem, że (nazwałem to kopiowaniem pamięci), ale jest to jeden z najbardziej czasochłonnych mechanizmów GC, w porównaniu do liczenia referencji. Jeśli więc pomaga się GC używać mechanizmu liczenia odwołań zamiast kopii na żywo, może to zapobiec powolnemu mechanizmowi kopiowania na żywo. –

+0

Kiedyś myślałem, co myślisz. Chciałbym poświęcić trochę czasu na sprawdzenie dokumentacji projektowej i dyskusji wokół aktualnych GC JVM. Zostały zaprojektowane bardzo silnie wokół typowych wzorów generowania śmieci. I chociaż pożądane jest odliczanie, to kończy się to tylko w bardzo wyjątkowych i nietypowych przypadkach. Zasadniczo muszę wypaczać mój projekt, aby uniknąć ŻADNYCH cykli w którymkolwiek z moich ścieżek odniesienia. Oczywiście, że to możliwe. Jednak jest to również ekstremalnie ograniczone do wszystkich, oprócz najprostszych modeli OO (przynajmniej w moim świecie). – chaotic3quilibrium

Powiązane problemy