Czy ustawienie obiektów Java na wartość null już coś robi?

112

Przeglądałem stare książki i znalazłem egzemplarz „Practical Java” Petera Haggera. W sekcji dotyczącej wydajności znajduje się zalecenie, aby ustawić odniesienia do obiektów, nullgdy nie są już potrzebne.

Czy w Javie ustawianie odwołań do obiektów nullpoprawia wydajność lub efektywność usuwania pamięci? Jeśli tak, w jakich przypadkach jest to problem? Klasy kontenerów? Kompozycja obiektu? Anonimowe klasy wewnętrzne?

Widzę to dość często w kodzie. Czy jest to przestarzała rada dotycząca programowania, czy nadal jest przydatna?

sal
źródło
2
Profiluj to. W nowoczesnych środowiskach wykonawczych nie powinno się zauważać żadnego znaczącego wzrostu wydajności ani zużycia pamięci.
Jason Coco
12
@Jason, Profil to? Zakłada się, że sprofiluję wystarczająco duży zestaw przypadków, aby uzyskać wystarczająco dobry zestaw wyników, aby odpowiedzieć na to pytanie. I że nie wybieram zestawu przypadków, w których maszyna wirtualna jest wystarczająco zoptymalizowana, aby maskować problemy z gc i wydajnością. Dlatego proszę o to tutaj. Zrozumieć przypadki, w których jest to problem.
Sal
1
Duplikat stackoverflow.com/questions/449409/… .
Michael Myers

Odpowiedzi:

73

To zależy trochę od tego, kiedy myślałeś o anulowaniu odniesienia.

Jeśli masz łańcuch obiektów A-> B-> C, to gdy A jest nieosiągalny, A, B i C będą wszystkie kwalifikować się do czyszczenia pamięci (zakładając, że nic innego nie odwołuje się do B lub C). Nie ma potrzeby i nigdy nie było potrzeby, aby na przykład jawnie ustawić odwołania A-> B lub B-> C na null.

Poza tym przez większość czasu problem tak naprawdę nie występuje, ponieważ w rzeczywistości masz do czynienia z obiektami w kolekcjach. Generalnie powinieneś zawsze myśleć o usunięciu obiektów z list, map itp. Poprzez wywołanie odpowiedniej metody remove ().

Przypadek, w którym kiedyś istniały porady dotyczące ustawiania odwołań na wartość null, dotyczył szczególnie długiego zakresu, w którym obiekt intensywnie wykorzystujący pamięć przestał być używany w części zakresu . Na przykład:

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;             <-- explicitly set to null
  doSomethingElse();
}

Uzasadnieniem tutaj było to, że ponieważ obj nadal zakres, a następnie bez wyraźnej Nulling odniesienia, to nie staje się śmieci kolekcjonerów aż po doSomethingElse () zakończy metod. I to jest rada, której prawdopodobnie już nie ma w nowoczesnych maszynach JVM : okazuje się, że kompilator JIT może ustalić, w którym momencie dane odwołanie do obiektu lokalnego nie jest już używane.

Neil Coffey
źródło
2
Zmienne lokalne mogą być optymalizowane przez maszynę. Zmienne klasowe nie mogą. Widziałem, że wątki robocze (w puli wątków) nie zwalniają swoich obiektów stanu po obsłużeniu żądania, a tym samym przypinają te obiekty do pamięci, dopóki wątek roboczy nie otrzyma nowego zadania (które natychmiast nadpisuje stare obiekty stanu nowymi). W przypadku dużych obiektów stanu i setek wątków roboczych (pomyśl: duży serwer http) może to oznaczać ogromne ilości pamięci przechowywanej i kopiowanej, ale nigdy nie będzie ona ponownie używana.
Brian White
1
Powinienem chyba tylko dodać: rada, której udzielam powyżej, jest radą do przestrzegania ogólnie, zakładając dobrze zachowujące się biblioteki, dobrze zachowującą się maszynę wirtualną itp. Jeśli okaże się, że masz, powiedzmy, bibliotekę puli wątków, która wisi na obiektach po zadanie zostało zakończone, no cóż ... to błąd we wspomnianej bibliotece puli wątków, który można obejść przez zerowanie odwołania do obiektu, ale równie dobrze można go obejść, używając mniej błędnej biblioteki puli wątków. Nie jestem pewien, czy taki błąd zmienia ogólne zasady projektowania.
Neil Coffey
25

Nie, to nie jest przestarzała rada. Wiszące odwołania nadal stanowią problem, zwłaszcza jeśli, powiedzmy, implementujesz kontener z rozszerzalną tablicą ( ArrayListlub podobny) przy użyciu wstępnie przydzielonej tablicy. Elementy wykraczające poza „logiczny” rozmiar listy powinny zostać usunięte, w przeciwnym razie nie zostaną zwolnione.

Zobacz Effective Java 2nd ed, Item 6: Eliminate Obsolete Object References.

Chris Jester-Young
źródło
Czy jest to problem z językiem czy z implementacją maszyny wirtualnej?
Pablo Santa Cruz
4
To kwestia „semantyczna”. Zasadniczo, ponieważ wstępnie przydzieliłeś tablicę, maszyna wirtualna to widzi. Nie wie nic o „logicznym” rozmiarze kontenera. Załóżmy, że masz ArrayList o rozmiarze 10, popartą 16-elementową tablicą. Maszyna wirtualna nie może wiedzieć, że pozycje 10–15 nie są w rzeczywistości używane; jeśli te sloty mają zawartość, nie zostaną zwolnione.
Chris Jester-Young
a co poza klasami kontenerów? W kompozycji obiektu, w której obiekt wewnętrzny nie jest usuwany przez obiekt zewnętrzny, jest.
Sal
7
@sal Ogólna zasada jest taka, że ​​jeśli nie możesz się do niego odwołać, zostanie on usunięty. Jeśli więc obiekt zewnętrzny zawiera odniesienie do innego obiektu, zakładając, że obiekt wewnętrzny nie ma żadnych innych odniesień, ustawienie jedynego odniesienia do obiektu zewnętrznego na wartość null spowoduje, że cały obiekt zostanie usunięty z pamięci, łącznie z jego osieroconymi odniesieniami.
ubermensch
2
W tym samym punkcie 6, o którym wspomniałeś: Wycofywanie odniesień do obiektów powinno być raczej wyjątkiem niż normą.
ACV
10

Pola instancji, elementy tablic

Jeśli istnieje odwołanie do obiektu, nie można go wyrzucić. Szczególnie jeśli ten obiekt (i cały wykres za nim) jest duży, istnieje tylko jedno odniesienie, które zatrzymuje czyszczenie pamięci i to odniesienie nie jest już potrzebne, jest to niefortunna sytuacja.

Przypadki patologiczne to obiekt, który zachowuje nieistotną instancję w całym drzewie XML DOM, które zostało użyte do jej skonfigurowania, komponent MBean, który nie został wyrejestrowany, lub pojedyncze odwołanie do obiektu z niezainstalowanej aplikacji internetowej, które zapobiega wyładowaniu całego modułu ładującego klasy .

Więc jeśli nie jesteś pewien, że obiekt, który przechowuje samo odniesienie, i tak zostanie usunięty jako śmieci (lub nawet wtedy), powinieneś wyzerować wszystko, czego już nie potrzebujesz.

Zmienne z zakresem:

Jeśli rozważasz ustawienie zmiennej lokalnej na wartość null przed końcem jej zakresu, aby mogła zostać odzyskana przez moduł wyrzucania elementów bezużytecznych i oznaczyć ją jako „bezużyteczną od teraz”, powinieneś zamiast tego rozważyć umieszczenie jej w bardziej ograniczonym zakresie .

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;          //   <-- explicitly set to null
  doSomethingElse();
}

staje się

{
  {  
     BigObject obj = ...
     doSomethingWith(obj);
  }    //         <-- obj goes out of scope
  doSomethingElse();
}

Długie, płaskie zakresy są również ogólnie złe dla czytelności kodu. Nie jest też niczym niezwykłym wprowadzenie prywatnych metod rozbijania rzeczy tylko w tym celu.

Thilo
źródło
Jeśli odnosisz się do silnego odniesienia, tak, to prawda, ale nie w przypadku wszystkich odniesień. Słabe odwołania w Javie mogą zostać usunięte z pamięci.
James Drinkard
5

Może to być przydatne w środowiskach o ograniczonej pamięci (np. Telefony komórkowe). Ustawiając wartość null, obiekt objetc nie musi czekać, aby zmienna wyszła poza zakres, aby została przypisana do elementu gc.

Jednak w codziennym programowaniu nie powinno to być regułą, z wyjątkiem szczególnych przypadków, takich jak ten, który przytoczył Chris Jester-Young.

besen
źródło
1

Po pierwsze, nie oznacza to niczego, do czego ustawiasz obiekt null. Wyjaśnię to poniżej:

List list1 = new ArrayList();
List list2 = list1;

W powyższym segmencie kodu tworzymy nazwę zmiennej odniesienia list1do ArrayListobiektu, która jest przechowywana w pamięci. Tak więc list1odnosi się do tego obiektu i jest to tylko zmienna. W drugim wierszu kodu kopiujemy odwołanie list1do list2. A teraz wracając do twojego pytania, jeśli to zrobię:

list1 = null;

oznacza list1to, że nie odnosi się już do żadnego obiektu, który jest przechowywany w pamięci, więc list2również nie będzie miał nic do odniesienia. Więc jeśli sprawdzisz rozmiar list2:

list2.size(); //it gives you 0

Tak więc pojawia się koncepcja garbage collector, która mówi: „Nie musisz się martwić o uwolnienie pamięci przechowywanej przez obiekt, zrobię to, gdy stwierdzę, że nie będzie już używany w programie, a JVM będzie mną zarządzać”.

Mam nadzieję, że wyjaśni to koncepcję.

Aman Goel
źródło
0

Jednym z powodów takiego działania jest wyeliminowanie przestarzałych odniesień do obiektów. Możesz przeczytać tekst tutaj.

Sudip Bhandari
źródło