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?
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?
Odpowiedzi:
Java korzysta z pamięci zarządzanej, więc jedynym sposobem przydzielenia pamięci jest użycie new
operatora, 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.
System.gc()
całkowicie ignorują .
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, null
aby 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.
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:
* „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) ).
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.
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 ).
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.
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.
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.
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).
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.
* „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.
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()
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.