Jak zwolnić pamięć w Javie?

146

Czy istnieje sposób na zwolnienie pamięci w Javie, podobny do free()funkcji C ? A może ustawienie obiektu na wartość null i poleganie na GC to jedyna opcja?

Felix
źródło
151
Ok ... wyjaśnijmy jedną rzecz. Tylko dlatego, że uważasz, że coś jest złą praktyką, a nie czymś, co zachęca do robienia, nie czyni tego wartym odrzucenia. To jasne i ważne pytanie, czy istnieje sposób na zwolnienie pamięci w Javie bez polegania na usuwaniu elementów bezużytecznych. Chociaż może to być zniechęcone i generalnie nieprzydatne lub niezbyt dobry pomysł, nie możesz wiedzieć, że nie ma scenariuszy, w których może to być wymagane, nie wiedząc, co wie Felix. Felix może nawet nie planował go używać. Może po prostu chcieć wiedzieć, czy to możliwe. W żaden sposób nie zasługuje na odrzucenie głosów.
Daniel Bingham
7
Dla wyjaśnienia, jest to skierowane do każdego, kto głosował za odrzuceniem - niekoniecznie poprzednie komentarze.
Daniel Bingham

Odpowiedzi:

96

Java korzysta z pamięci zarządzanej, więc jedynym sposobem przydzielenia pamięci jest użycie newoperatora, a jedynym sposobem na zwolnienie pamięci jest użycie modułu odśmiecania pamięci.

To zarządzanie pamięcią oficjalny dokument (PDF) może pomóc wyjaśnić, co się dzieje.

Możesz również zadzwonić, System.gc()aby zasugerować natychmiastowe uruchomienie modułu odśmiecania pamięci. Jednak ostateczną decyzję podejmuje Java Runtime, a nie kod.

Zgodnie z dokumentacją Java ,

Wywołanie metody gc sugeruje, że wirtualna maszyna Java poświęca wysiłek na odtworzenie nieużywanych obiektów, aby udostępnić pamięć, którą obecnie zajmują, do szybkiego ponownego wykorzystania. Kiedy kontrola wraca z wywołania metody, wirtualna maszyna języka Java dołożyła wszelkich starań, aby odzyskać miejsce ze wszystkich odrzuconych obiektów.

Daniel Pryden
źródło
5
Wymusza to działanie Garbage Collectora. Nie zmusza to jednak do zwolnienia pamięci ...
Pablo Santa Cruz
13
Nie Pablo, to nie zmusza GC do działania.
Jesper
1
Bardzo wiarygodna osoba powiedziała mi, że wszystkie śmieciarze HotSpotVM System.gc()całkowicie ignorują .
Esko,
1
W winXp java SE GC uruchamia każdą System.gc () lub prawie każdą, ale dokument API tego nie gwarantuje.
teodozjan
2
@Pablo Santa Cruz Co masz na myśli mówiąc, że nie zwalnia pamięci? Właśnie przetestowałem to w moim programie, który wydawał się mieć wyciek, a użycie pamięci RAM wydawało się stabilizować? Daniel właśnie powiedział, że to tylko sugeruje, dlaczego więc procent używanego barana zawsze stabilizował się za każdym razem, gdy wywoływałem metodę. Mylicie mnie ludzie.
65

Wydaje się, że nikt nie wspomniał wprost o ustawianiu odniesień do obiektów null, co jest uzasadnioną techniką „zwalniania” pamięci, którą warto rozważyć.

Na przykład, załóżmy, że zadeklarowałeś List<String>na początku metody, która rozrosła się i była bardzo duża, ale była wymagana tylko do połowy metody. W tym momencie można ustawić odwołanie do listy, nullaby umożliwić modułowi wyrzucania elementów bezużytecznych potencjalnie odzyskanie tego obiektu przed zakończeniem metody (i mimo to odwołanie wykracza poza zakres).

Zauważ, że w rzeczywistości rzadko używam tej techniki, ale warto to rozważyć, gdy mam do czynienia z bardzo dużymi strukturami danych.

Adamskiego
źródło
8
Jeśli naprawdę wykonujesz dużo pracy na obiekcie, który jest używany tylko w części metody, to proponuję; twoja metoda jest zbyt skompilowana, podziel metodę na części przed i po lub użyj bloku dla pierwszej połowy kodu (późniejsza jest bardziej przydatna dla skryptów testowych)
Peter Lawrey
5
Miejsce, w którym ustawienie odwołania do obiektu na wartość null jest ważne, jest wtedy, gdy odwołuje się do niego z innego długowiecznego obiektu (lub prawdopodobnie ze statycznej zmiennej). Na przykład, jeśli masz tablicę dużych obiektów o długiej żywotności i przestajesz używać jednego z tych obiektów, powinieneś ustawić odwołanie do tablicy na null, aby obiekt był dostępny dla GC.
Hot Licks
22
System.gc(); 

Uruchamia moduł odśmiecania pamięci.

Wywołanie metody gc sugeruje, że maszyna wirtualna Java poświęca wysiłek na odtworzenie nieużywanych obiektów w celu udostępnienia pamięci, którą obecnie zajmują, do szybkiego ponownego wykorzystania. Kiedy kontrola wraca z wywołania metody, wirtualna maszyna języka Java dołożyła wszelkich starań, aby odzyskać miejsce ze wszystkich odrzuconych obiektów.

Niepolecane.

Edycja: napisałem oryginalną odpowiedź w 2009 roku. Jest teraz 2015.

Urządzenia do usuwania śmieci stawały się coraz lepsze w ciągu ~ 20 lat istnienia Java. W tym momencie, jeśli ręcznie wywołujesz moduł odśmiecania pamięci, możesz rozważyć inne podejścia:

  • Jeśli zmuszając GC na ograniczonej liczbie komputerów, może warto o temperaturze równoważenia obciążenia z dala z od bieżącej maszyny, czekając, aż zakończy obsługę podłączonych klientów, limit czasu po pewnym czasie dla zawieszania połączeń, a następnie po prostu trudny - uruchom ponownie maszynę JVM. To okropne rozwiązanie, ale jeśli patrzysz na System.gc (), wymuszone ponowne uruchomienia mogą być możliwym problemem.
  • Rozważ użycie innego garbage collectora. Na przykład (nowy w ciągu ostatnich sześciu lat) kolektor G1 jest modelem o niskiej przerwie; zużywa ogólnie więcej procesora, ale najlepiej jest nigdy nie wymuszać twardego zatrzymania wykonania. Ponieważ obecnie prawie wszystkie procesory serwerowe mają wiele rdzeni, jest to naprawdę dobry kompromis.
  • Spójrz na swoje flagi dostrajające użycie pamięci. Szczególnie w nowszych wersjach Javy, jeśli nie masz tak wielu długoterminowo działających obiektów, rozważ zwiększenie rozmiaru newgen w stercie. newgen (młody) to miejsce, w którym przydzielane są nowe obiekty. W przypadku serwera WWW wszystko, co zostało utworzone na żądanie, jest tutaj umieszczane, a jeśli ta przestrzeń jest zbyt mała, Java poświęci dodatkowy czas na aktualizację obiektów do pamięci o dłuższej żywotności, gdzie ich zabicie jest droższe. (Jeśli newgen jest trochę za mały, zapłacisz za to.) Na przykład w G1:
    • XX: G1NewSizePercent (domyślnie 5; prawdopodobnie nie ma znaczenia).
    • XX: G1MaxNewSizePercent (domyślnie 60; prawdopodobnie podnieś tę wartość).
  • Zastanów się nad powiedzeniem śmieciarzowi, że nie jest w porządku z dłuższą przerwą. Spowoduje to częstsze uruchamianie GC, aby umożliwić systemowi utrzymanie pozostałych ograniczeń. W G1:
    • XX: MaxGCPauseMillis (domyślnie 200.)
Dean J.
źródło
1
Komentując mój własny post, to często nic nie daje, a wielokrotne wywoływanie go może spowodować niestabilność maszyny JVM i tak dalej. Może również przejechać po psie; podchodź ostrożnie.
Dean J
1
Położyłbym duży nacisk na „sugeruje” część „Wywoływanie metody gc sugeruje, że wysiłek związany z rozszerzeniem JVM”
mat b
2
@Jesper, odpowiedź Deana brzmi „sugeruje”. W rzeczywistości opublikował dokładną dokumentację z javadocs metody ...
matt b
2
@Software Monkey: Tak, mogłem go po prostu edytować. Ale ponieważ Dean J był oczywiście aktywny (publikował tylko kilka minut temu), pomyślałem, że to grzeczność poprosić go o zrobienie tego. Gdyby tego nie zrobił, wróciłbym tutaj, dokonał edycji i usunął mój komentarz.
Daniel Pryden,
1
Warto też powiedzieć, DLACZEGO nie jest to zalecane. Jeśli JVM zwróci uwagę na „sugestię” uruchomienia GC, prawie na pewno spowolni to działanie Twojej aplikacji, prawdopodobnie o wiele rzędów wielkości!
Stephen C.
11

* „Osobiście polegam na zerowaniu zmiennych jako symbolu zastępczego dla przyszłego prawidłowego usunięcia. Na przykład, poświęcam trochę czasu, aby anulować wszystkie elementy tablicy przed faktycznym usunięciem (nadaniem wartości zerowej) samej tablicy.”

To jest niepotrzebne. Sposób działania Java GC polega na tym, że znajduje obiekty, które nie mają do nich odniesienia, więc jeśli mam obiekt x z odwołaniem (= zmienną) a, które na niego wskazuje, GC go nie usunie, ponieważ istnieje odniesienie do tego obiektu:

a -> x

Jeśli wyzerujesz a, to się stanie:

a -> null
     x

Więc teraz x nie ma odniesienia do niego wskazującego i zostanie usunięte. To samo dzieje się, gdy ustawisz a jako odniesienie do innego obiektu niż x.

Więc jeśli masz tablicę arr, która odwołuje się do obiektów x, y i z oraz zmienną a, która odwołuje się do tablicy, wygląda to tak:

a -> arr -> x
         -> y
         -> z

Jeśli wyzerujesz a, to się stanie:

a -> null
     arr -> x
         -> y
         -> z

Więc GC znajduje arr, który nie ma ustawionego odniesienia i usuwa go, co daje następującą strukturę:

a -> null
     x
     y
     z

Teraz GC znajduje x, y i z i usuwa je również. Zerowanie każdego odniesienia w tablicy nie poprawi niczego, po prostu zużyje czas procesora i miejsce w kodzie (to powiedziawszy, nie zaszkodzi bardziej niż to. GC nadal będzie w stanie działać tak, jak powinien) ).

Dakkaron
źródło
5

Ważnym powodem chęci zwolnienia pamięci z dowolnego programu (java lub nie) jest udostępnienie większej ilości pamięci innym programom na poziomie systemu operacyjnego. Jeśli moja aplikacja java używa 250 MB, mogę chcieć zmniejszyć ją do 1 MB i udostępnić 249 MB innym aplikacjom.

Yios
źródło
Jeśli potrzebujesz jawnie zwolnić fragment 249 MB w programie Java, zarządzanie pamięcią nie byłoby pierwszą rzeczą, nad którą chciałbym pracować.
Marc DiMillo,
3
Ale zwolnienie miejsca w stercie Java nie powoduje (w ogólnym przypadku) udostępnienia go innym aplikacjom.
Hot Licks
5

Aby rozszerzyć odpowiedź i komentarz Yiannisa Xanthopoulosa i Hot Licks (przepraszam, nie mogę jeszcze komentować!), Możesz ustawić opcje maszyny wirtualnej w następujący sposób:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

W moim jdk 7 spowoduje to zwolnienie nieużywanej pamięci maszyny wirtualnej, jeśli ponad 30% sterty zostanie zwolnione po GC, gdy maszyna wirtualna jest bezczynna. Prawdopodobnie będziesz musiał dostroić te parametry.

Chociaż nie widziałem tego podkreślonego w linku poniżej, zauważ, że niektóre garbage collectors mogą nie przestrzegać tych parametrów i domyślnie java może wybrać jeden z nich, jeśli masz więcej niż jeden rdzeń (stąd powyższy argument UseG1GC ).

Argumenty maszyny wirtualnej

Aktualizacja: W przypadku Java 1.8.0_73 widziałem, jak JVM od czasu do czasu wypuszczał niewielkie kwoty z domyślnymi ustawieniami. Wydaje się, że robi to tylko wtedy, gdy ~ 70% sterty jest nieużywanych .. nie wiem, czy zwolnienie byłoby bardziej agresywne, gdyby system operacyjny miał mało pamięci fizycznej.

nsandersen
źródło
4

Zrobiłem na tym eksperymenty.

To prawda, że System.gc();tylko sugeruje uruchomienie garbage collectora.

Ale wywołanie System.gc();po ustawieniu wszystkich odniesień na null, poprawi wydajność i wykorzystanie pamięci.

Hemant Yadav
źródło
Myślę, że nie można powiedzieć na pewno, że „wywołanie System.gc (); po ustawieniu wszystkich odwołań na wartość null poprawi wydajność i wykorzystanie pamięci.”. Ponieważ istnieje ogromna złożoność obliczeniowa System.gc (). I nawet po wywołaniu System.gc () i rzeczywiście zebraniu śmieci, jvm może nie zwrócić pamięci z powrotem do systemu operacyjnego lub systemu. JVM może zachować pamięć do wykorzystania w przyszłości. Zobacz tę odpowiedź .
Md. Abu Nafee Ibna Zahid
3

Jeśli naprawdę chcesz przydzielić i zwolnić blok pamięci, możesz to zrobić za pomocą bezpośrednich ByteBuffers. Istnieje nawet nieprzenośny sposób na zwolnienie pamięci.

Jednak, jak zasugerowano, tylko dlatego, że musisz zwolnić pamięć w C, nie oznacza, że ​​warto to zrobić.

Jeśli uważasz, że naprawdę masz dobry przypadek użycia za darmo (), uwzględnij go w pytaniu, abyśmy mogli zobaczyć, co zamierzasz zrobić, jest całkiem prawdopodobne, że istnieje lepszy sposób.

Peter Lawrey
źródło
3

W całości z javacoffeebreak.com/faq/faq0012.html

Wątek o niskim priorytecie automatycznie zajmuje się wyrzucaniem elementów bezużytecznych dla użytkownika. W czasie bezczynności wątek może zostać wywołany i może zacząć zwalniać pamięć przydzieloną wcześniej obiektowi w Javie. Ale nie martw się - nie usunie twoich obiektów!

Gdy nie ma odniesień do obiektu, staje się to uczciwą grą dla garbage collectora. Zamiast wywoływać jakąś procedurę (na przykład free w C ++), po prostu przypisujesz wszystkie odwołania do obiektu na wartość null lub przypisujesz nową klasę do referencji.

Przykład:

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

Jeśli Twój kod ma zażądać dużej ilości pamięci, możesz zażądać od modułu odśmiecania pamięci, aby zaczął odzyskiwać miejsce, zamiast pozwalać mu na to jako wątek o niskim priorytecie. Aby to zrobić, dodaj następujący kod do swojego kodu

System.gc();

Moduł odśmiecania pamięci podejmie próbę odzyskania wolnego miejsca, a aplikacja może kontynuować działanie z odzyskaniem jak największej ilości pamięci (na niektórych platformach mogą wystąpić problemy z fragmentacją pamięci).

wyświetlana nazwa
źródło
1

W moim przypadku, ponieważ mój kod Java ma zostać przeniesiony na inne języki w niedalekiej przyszłości (głównie C ++), przynajmniej chcę zapłacić frazę za prawidłowe zwolnienie pamięci, aby pomóc później w procesie przenoszenia.

Osobiście polegam na zerowaniu zmiennych jako symbolu zastępczym dla przyszłego prawidłowego usunięcia. Na przykład poświęcam trochę czasu, aby unieważnić wszystkie elementy tablicy, zanim faktycznie usunę (uczynię null) samą tablicę.

Ale mój przypadek jest bardzo szczególny i wiem, że robiąc to, odbieram hity wydajności.

Oskuro
źródło
1

* „Na przykład, powiedzmy, że zadeklarowałeś List na początku metody, która rozrosła się i stała się bardzo duża, ale była wymagana tylko do połowy metody. W tym momencie możesz ustawić odwołanie List na wartość null aby umożliwić modułowi odśmiecania pamięci potencjalnie odzyskanie tego obiektu przed zakończeniem metody (a odwołanie i tak wykracza poza zakres). " *

To prawda, ale tego rozwiązania nie da się uogólnić. Podczas ustawiania odwołania do obiektu List na null -will- udostępni pamięć do czyszczenia pamięci, jest to prawdą tylko dla obiektu List typów pierwotnych. Jeśli zamiast tego obiekt List zawiera typy odwołań, ustawienie obiektu List = null nie spowoduje wyłuskiwania -dowolnych- typów odwołań zawartych na liście. W takim przypadku ustawienie obiektu List = null spowoduje osierocenie zawartych typów odwołań, których obiekty nie będą dostępne do czyszczenia pamięci, chyba że algorytm czyszczenia pamięci jest wystarczająco inteligentny, aby określić, że obiekty zostały osierocone.

Gothri
źródło
1
W rzeczywistości to nieprawda. Moduł odśmiecania pamięci Java jest wystarczająco inteligentny, aby poprawnie to obsłużyć. Jeśli wyzerujesz listę (a obiekty na liście nie mają innych odniesień do nich), GC może odzyskać wszystkie obiekty na liście. Może zdecydować się tego nie robić w chwili obecnej, ale w końcu je odzyska. To samo dotyczy cyklicznych odniesień. Zasadniczo sposób działania GC polega na ukrytym poszukiwaniu osieroconych obiektów, a następnie ich odzyskiwaniu. Na tym polega cała praca GC. Sposób, w jaki to opisujesz, uczyniłby GC całkowicie bezużytecznym.
Dakkaron,
1

Chociaż java zapewnia automatyczne czyszczenie pamięci czasami będziesz chciał wiedzieć, jak duży jest obiekt i ile go pozostało. Zwolnij pamięć używając programowo import java.lang;i Runtime r=Runtime.getRuntime(); aby uzyskać wartości pamięci używając mem1=r.freeMemory();do zwolnienia pamięci wywołaj r.gc();metodę i wywołaniefreeMemory()

Benzoes
źródło
1

Rekomendacją z JAVA jest przypisanie do null

Z https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

Jawne przypisanie wartości null zmiennym, które nie są już potrzebne, pomaga modułowi wyrzucania elementów bezużytecznych w identyfikowaniu części pamięci, które można bezpiecznie odzyskać. Chociaż Java zapewnia zarządzanie pamięcią, nie zapobiega wyciekom pamięci ani wykorzystaniu nadmiernej ilości pamięci.

Aplikacja może powodować wycieki pamięci, nie zwalniając odwołań do obiektów. Takie postępowanie zapobiega odzyskaniu tych obiektów przez moduł odśmiecania pamięci Java i powoduje zwiększenie ilości używanej pamięci. Jawne niwelowanie odwołań do zmiennych po ich użyciu umożliwia modułowi odśmiecania pamięci odzyskanie pamięci.

Jednym ze sposobów wykrywania wycieków pamięci jest stosowanie narzędzi do profilowania i wykonywanie migawek pamięci po każdej transakcji. Aplikacja zabezpieczona przed wyciekami w stanie ustalonym będzie pokazywać stałą aktywną pamięć sterty po usunięciu elementów bezużytecznych.

user3869837
źródło