Kiedy System.gc () coś robi?

118

Wiem, że w Javie zbieranie śmieci jest zautomatyzowane. Ale zrozumiałem, że jeśli wywołasz System.gc()w swoim kodzie, że JVM może, ale nie musi, zdecydować o przeprowadzeniu czyszczenia pamięci. Jak to dokładnie działa? Na jakiej podstawie / parametrach dokładnie maszyna JVM decyduje się wykonać (lub nie) wykonać GC, gdy widzi System.gc()?

Czy są jakieś przykłady, w których przypadku warto umieścić to w swoim kodzie?

Michael
źródło
4
Zbyt złożona koncepcja, aby opisać szczegóły, ale ten artykuł powinien ci pomóc: chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET
Dokładne zachowanie zależy od maszyny JVM, tj. Zależy od implementacji.
Thorbjørn Ravn Andersen

Odpowiedzi:

59

W praktyce zazwyczaj decyduje się na usuwanie elementów bezużytecznych. Odpowiedź różni się w zależności od wielu czynników, takich jak JVM, na którym działa, w jakim trybie się znajduje i jakiego algorytmu czyszczenia pamięci używa.

Nie polegałbym na tym w twoim kodzie. Jeśli JVM ma wyrzucić błąd OutOfMemoryError, wywołanie System.gc () go nie zatrzyma, ponieważ moduł odśmiecania pamięci będzie próbował uwolnić jak najwięcej, zanim osiągnie skrajność. Jedyny raz, kiedy widziałem, jak jest używany w praktyce, to w IDE, gdzie jest dołączony do przycisku, który użytkownik może kliknąć, ale nawet tam nie jest to zbyt przydatne.

jodonnell
źródło
3
możesz wymusić wywóz śmieci w jconsole
Benedikt Waldvogel
25
@bene Czy naprawdę możesz to „wymusić”? Czy jconsole po prostu wywołuje System.gc ()?
John Meagher,
4
Używałbym System.gc()na ekranie ładowania gry wideo. W tym momencie nie obchodzi mnie, czy wezwanie wyczyści wszystko, co możliwe, lub w ogóle nic nie zrobi. Chciałbym jednak wolę , że „wysiłek poświęcać kierunku recykling nieużywanych obiektów” podczas ekranie ładowania, a nie w czasie rozgrywki.
Kröw
30

Jedynym przykładem, w którym mogę wymyślić sens wywoływanie System.gc (), jest profilowanie aplikacji w celu wyszukiwania możliwych wycieków pamięci. Uważam, że osoby odpowiedzialne za profilowanie wywołują tę metodę tuż przed zrobieniem migawki pamięci.

Guillermo Vasconcelos
źródło
4
Tak, to też robię. Wartości zwracane przez Runtime.freeMemory () np. Są naprawdę znaczące tylko po System.gc (), ponieważ zwykle chcesz wiedzieć, ile pamięci jest blokowane przez instancje, których nie można pobrać. Oczywiście do użytku tylko w debugowaniu / profilowaniu pamięci, nigdy w produkcji.
sleske
Nie, jest to również dobre dla wielu innych scenariuszy. Na przykład, powiedzmy, że masz pojedynczy wzorzec uwzględniający cykl życia. W onStop () jeśli jesteś null (ing) instancji, dobrym pomysłem jest wywołanie System.gc (), aby jej pomóc.
portfoliobuilder
26

Specyfikacja języka Java nie gwarantuje, że maszyna JVM uruchomi GC po wywołaniu System.gc(). To jest powód tego „może, ale nie musi, zdecydować się na przeprowadzenie GC w tym momencie”.

Teraz, jeśli spojrzysz na kod źródłowy OpenJDK , który jest kręgosłupem Oracle JVM, zobaczysz, że wywołanie wywołania System.gc()rozpoczyna cykl GC. Jeśli używasz innej maszyny JVM, takiej jak J9, musisz sprawdzić jej dokumentację, aby znaleźć odpowiedź. Na przykład maszyna JVM firmy Azul ma moduł wyrzucania elementów bezużytecznych, który działa w sposób ciągły, więc wywołanie System.gc()nic nie da

Inne odpowiedzi wspominają o uruchomieniu GC w JConsole lub VisualVM. Zasadniczo te narzędzia wykonują zdalne wywołanie System.gc().

Zwykle nie chcesz rozpoczynać cyklu wyrzucania elementów bezużytecznych z kodu, ponieważ wprowadza on w błąd semantykę aplikacji. Twoja aplikacja robi pewne rzeczy biznesowe, JVM zajmuje się zarządzaniem pamięcią. Powinieneś oddzielić te obawy (nie zmuszaj aplikacji do zarządzania pamięcią, skup się na biznesie).

Jednak jest kilka przypadków, w których wezwanie System.gc()może być zrozumiałe. Rozważmy na przykład mikroznak. Nikt nie chce, aby cykl GC odbywał się w środku mikroznaku. Możesz więc wyzwolić cykl GC między każdym pomiarem, aby upewnić się, że każdy pomiar zaczyna się od pustej sterty.

Pierre Laporte
źródło
26

Nie masz kontroli nad GC w java - decyduje maszyna wirtualna. Nigdy nie trafiłem na przypadek, w którym System.gc()jest to potrzebne . Ponieważ System.gc()wywołanie po prostu SUGERUJE, że maszyna wirtualna usuwa elementy bezużyteczne, a także wykonuje PEŁNE wyrzucanie elementów bezużytecznych (stare i nowe generacje w stosie wielopokoleniowym), może to spowodować zużycie WIĘCEJ cykli procesora niż jest to konieczne.

W niektórych przypadkach warto zasugerować maszynie wirtualnej, aby zrobiła całą kolekcję TERAZ, ponieważ być może wiesz, że aplikacja będzie bezczynna przez kilka następnych minut, zanim nastąpi duże obciążenie. Na przykład, zaraz po zainicjowaniu wielu tymczasowych obiektów podczas uruchamiania aplikacji (tj. Właśnie zapisałem w pamięci podręcznej TONĘ informacji i wiem, że przez minutę lub więcej nie będę miał dużo aktywności). Pomyśl o IDE, takim jak uruchamianie zaćmienia - inicjalizuje ono wiele, więc być może natychmiast po inicjalizacji sensowne jest wykonanie pełnego gc w tym momencie.

DustinB
źródło
17

Musisz bardzo uważać, kiedy dzwonisz System.gc(). Wywołanie go może spowodować niepotrzebne problemy z wydajnością w aplikacji i nie gwarantuje rzeczywistego wykonania kolekcji. W rzeczywistości można wyłączyć jawneSystem.gc() za pomocą argumentu java -XX:+DisableExplicitGC.

Gorąco polecam przeczytanie dokumentów dostępnych w Java HotSpot Garbage Collection, aby uzyskać bardziej szczegółowe informacje na temat czyszczenia pamięci.

David Schlosnagle
źródło
Moja aplikacja na Androida zawsze zgłasza wyjątek OutOfMemoryException. Więc myślałem o użyciu System.gc () w funkcji onDestroy mojej klasy, aby usunąć wszystkie niechciane odniesienia i obiekty. Czy to dobre podejście
Sagar Devanga
2
@Sagar Devanga Ręczne dzwonienie do gc, tak jak opisujesz, raczej nie pomoże. Przed rzuceniem OOM JVM będzie już próbował zebrać śmieci, aby zwolnić pamięć
Scrubbie
10

System.gc()jest implementowana przez maszynę wirtualną, a jej działanie zależy od implementacji. Na przykład osoba wdrażająca może po prostu wrócić i nic nie robić.

Jeśli chodzi o wydanie ręcznej zbiórki, jedynym momentem, w którym możesz chcieć to zrobić, jest porzucenie dużej kolekcji zawierającej mnóstwo mniejszych kolekcji - Map<String,<LinkedList>> na przykład - i chcesz spróbować wtedy osiągnąć perfekcyjny hit i tam, ale w większości nie powinieneś się tym martwić. GC wie lepiej niż ty - niestety - przez większość czasu.

Patrick
źródło
8

Jeśli używasz bezpośrednich buforów pamięci, JVM nie uruchomi GC za Ciebie, nawet jeśli masz mało pamięci bezpośredniej.

Jeśli zadzwonisz ByteBuffer.allocateDirect()i otrzymasz OutOfMemoryError, możesz stwierdzić, że to wywołanie jest w porządku po ręcznym uruchomieniu GC.

Peter Lawrey
źródło
Mówisz, że System.gc () zbiera bezpośrednie alokacje, podczas gdy JVM zwykle tego nie robi (przed wyrzuceniem OOM). Ponieważ uważam, że to źle. Jedynym powodem, dla którego alokacja może się powieść po jawnym GC, jest dodatkowy czas i większe prawdopodobieństwo, że obiekt w stercie z finalizatorem zostanie zwolniony (i zwolni niektóre bezpośrednio przydzielone obiekty).
eckes
W najnowszej wersji Java 6 kod w assignateBuffer zawsze uruchamia metodę System.gc () przed wyrzuceniem błędu OutOfMemoryError. W kodzie finalizacyjnym ma skrót, Cleanersktórego używa pamięć bezpośrednia. tj. nie jest zwalniany przez wątek finalizujący, ponieważ może to być zbyt wolne. Mimo to możliwe jest uzyskanie OOME, jeśli szczególnie szybko tworzysz bezpośrednie regiony pamięci. Rozwiązaniem jest, aby tego nie robić i ponownie wykorzystywać swoje bezpośrednie regiony pamięci tam, gdzie jest to możliwe.
Peter Lawrey
1
dzięki, to bardziej przypomina moje doświadczenie. Więc myślę, że odpowiedź jest poprawna tylko dla starszych wersji Java.
eckes
5

Większość maszyn JVM uruchomi GC (w zależności od przełącznika -XX: DiableExplicitGC i -XX: + ExplicitGCInvokesConcurrent). Ale specyfikacja jest po prostu gorzej zdefiniowana, aby później umożliwić lepsze implementacje.

Specyfikacja wymaga wyjaśnienia: Bug # 6668279: (specyfikacja) System.gc () powinien wskazywać, że nie zalecamy używania i nie gwarantujemy zachowania

Wewnętrznie metoda gc jest używana przez RMI i NIO i wymagają one synchronicznego wykonywania, co: jest obecnie omawiane:

Błąd nr 5025281: Zezwalaj System.gc () na wyzwalanie współbieżnych (bez zatrzymywania świata) pełnych kolekcji

eckes
źródło
3

Garbage Collectionjest dobry Java, jeśli wykonujemy oprogramowanie zakodowane w Javie na komputerze stacjonarnym / laptopie / serwerze. Można zadzwonić System.gc()lub Runtime.getRuntime().gc()w Java.

Pamiętaj tylko, że żadne z tych połączeń nie gwarantuje nic. To tylko sugestia dla jvm, aby uruchomić Garbage Collectora. To zależy od JVM, czy obsługuje GC, czy nie. Krótka odpowiedź: nie wiemy, kiedy działa. Dłuższa odpowiedź: JVM uruchomiłoby gc, gdyby miał na to czas.

Myślę, że to samo dotyczy Androida. Może to jednak spowolnić system.

Naveen
źródło
JVM uruchomiłoby gc, gdyby miał na to czas : nie zawsze prawda, Jvm może uruchomić gc, gdy pamięć Eden jest pełna.
abdelmouheimen
2

Zwykle maszyna wirtualna automatycznie wyrzucałaby elementy bezużyteczne przed wyrzuceniem wyjątku OutOfMemoryException, więc dodanie jawnego wywołania nie powinno pomóc, z wyjątkiem tego, że być może przenosi działanie wydajności na wcześniejszy moment.

Myślę jednak, że spotkałem się z przypadkiem, w którym może to mieć znaczenie. Nie jestem jednak pewien, ponieważ jeszcze nie przetestowałem, czy to ma jakikolwiek efekt:

Kiedy mapujesz plik w pamięci, uważam, że wywołanie map () zgłasza IOException, gdy wystarczająco duży blok pamięci jest niedostępny. Myślę, że czyszczenie pamięci tuż przed plikiem map () mogłoby temu zapobiec. Co myślisz?


źródło
2

nigdy nie możemy wymusić czyszczenia pamięci. System.gc sugeruje tylko vm do czyszczenia pamięci, jednak tak naprawdę, o której godzinie działa mechanizm, nikt nie wie, tak jest w specyfikacji JSR.

lwpro2
źródło
2

Jest wiele do powiedzenia w poświęceniu czasu na przetestowanie różnych ustawień czyszczenia pamięci, ale jak wspomniano powyżej, zwykle nie jest to przydatne.

Obecnie pracuję nad projektem obejmującym środowisko o ograniczonej pamięci i stosunkowo duże ilości danych - jest kilka dużych fragmentów danych, które przesuwają moje środowisko do granic możliwości i mimo że udało mi się zmniejszyć zużycie pamięci, więc że teoretycznie powinno działać dobrze, nadal otrzymywałbym błędy dotyczące miejsca na stertę - pełne opcje GC pokazały mi, że próbuje zbierać śmieci, ale bezskutecznie. W debugerze mogłem wykonać System.gc () i na pewno byłoby „dużo” dostępnej pamięci ... niewiele dodatkowej, ale wystarczającej.

W rezultacie moja aplikacja wywołuje System.gc () tylko wtedy, gdy ma zamiar wejść do segmentu kodu, w którym zostaną przydzielone duże bufory niezbędne do przetwarzania danych, a test na dostępnej wolnej pamięci wskazuje, że nie gwarantujemy to. W szczególności patrzę na środowisko 1 GB, w którym co najmniej 300 MB jest zajmowane przez dane statyczne, przy czym większość danych niestatycznych jest związanych z wykonywaniem, z wyjątkiem sytuacji, gdy przetwarzane dane mają co najmniej 100-200 MB w źródło. To wszystko jest częścią procesu automatycznej konwersji danych, więc dane istnieją przez stosunkowo krótki czas w dłuższej perspektywie.

Niestety, chociaż dostępne są informacje o różnych opcjach strojenia garbage collectora, wydaje się, że jest to w dużej mierze proces eksperymentalny, a szczegóły niższego poziomu potrzebne do zrozumienia, jak radzić sobie z tymi konkretnymi sytuacjami, nie są łatwe.

Wszystko to zostało powiedziane, mimo że używam System.gc (), nadal dostrajałem się za pomocą parametrów wiersza poleceń i udało mi się poprawić ogólny czas przetwarzania mojej aplikacji o stosunkowo znaczną ilość, mimo że nie mogłem obejść przeszkodą wynikającą z pracy z większymi blokami danych. Biorąc to pod uwagę, System.gc () jest narzędziem ... bardzo zawodnym narzędziem, a jeśli nie jesteś ostrożny z tym, jak go używasz, będziesz żałować, że nie działało częściej niż nie.

Kyune
źródło
2

Jeśli chcesz wiedzieć, czy ktoś dzwoni do Ciebie System.gc(), dzięki nowej aktualizacji 4 oprogramowania Java 7 możesz otrzymać powiadomienie, gdy JVM przeprowadza czyszczenie pamięci.

Nie jestem jednak w 100% pewien, że klasa GarbageCollectorMXBean została wprowadzona w aktualizacji 4 Java 7, ponieważ nie mogłem jej znaleźć w informacjach o wydaniu, ale znalazłem informacje w witrynie javaperformancetuning .com

Shervin Asgari
źródło
0

Nie przychodzi mi do głowy konkretny przykład, kiedy dobrze jest uruchomić jawny GC.

Ogólnie rzecz biorąc, uruchomienie jawnego GC może w rzeczywistości spowodować więcej szkody niż pożytku, ponieważ jawne GC wyzwoli pełną kolekcję, która trwa znacznie dłużej, ponieważ przechodzi przez każdy obiekt. Jeśli ten jawny gc zostanie wywołany wielokrotnie, może to łatwo doprowadzić do spowolnienia aplikacji, ponieważ spędza dużo czasu na uruchamianiu pełnych GC.

Alternatywnie, jeśli przeglądasz stertę za pomocą analizatora sterty i podejrzewasz, że komponent biblioteki wywołuje jawne GC, możesz go wyłączyć, dodając: gc = -XX: + DisableExplicitGC do parametrów JVM.

zxcv
źródło
2
Wywołujemy System.gc (), aby spróbować zamknąć nasze obiekty MappedByteBuffer, które w przeciwnym razie nie są zamknięte, a zatem nie są dostępne dla innych procesów lub do obcięcia plików, nawet jeśli wywołujemy metodę „close ()” na obiektach. Niestety nadal nie zawsze je zamyka.
Tim Cooper
0

Zgodnie z Thinking in Java Bruce Eckel, jeden przypadek użycia dla jawnego System.gc () wywołania jest wymuszenie finalizacji, czyli wywołanie metody finalize .

Gwiazdka
źródło
Uwaga: istnieje własna metoda wymuszania biegu finalizacji. (Problem w rzeczywistości nie polega na uruchomieniu finalizatorów, ale na rozpoznaniu, że obiekt może zostać sfinalizowany, ponieważ nie ma do niego odniesień)
eckes,
-1

podczas gdy system.gc działa, zatrzyma świat: wszystkie odpowiedzi są zatrzymywane, więc garbage collector może przeskanować każdy obiekt, aby sprawdzić, czy jest potrzebny usunięty. jeśli aplikacja jest projektem sieciowym, wszystkie żądania są zatrzymywane do zakończenia działania gc, co spowoduje, że projekt sieciowy nie będzie działał w trybie monenta.

fleture
źródło