Чи встановлення об’єктів Java на нуль більше нічого не робить?


112

Я переглядав кілька старих книг і знайшов примірник «Практичної Java» Пітера Хаггера. У розділі продуктивності є рекомендація встановлювати посилання на об'єкти, nullколи вони більше не потрібні.

Чи встановлює в Java налаштування посилань на об'єкти для nullпідвищення продуктивності чи збору сміття? Якщо так, то в яких випадках це питання? Контейнерні класи? Об'єктна композиція? Анонімні внутрішні класи?

Я бачу це в коді досить часто. Це тепер застаріла порада з програмування чи вона все ще корисна?


2
Профілюйте це. У сучасних умовах роботи ви не повинні бачити значного збільшення продуктивності чи пам’яті пам’яті.
Джейсон Коко

12
@Jason, профіль? Це передбачає, що я профайлюю досить великий набір справ, щоб отримати достатньо хороший результат, щоб відповісти на це. І що я не вибираю набір випадків, в яких VM є оптимізованим для маскування gc та проблем продуктивності. Ось чому я прошу це тут. Щоб зрозуміти випадки, коли це питання.
сл

Відповіді:


73

Це трохи залежить від того, коли ви думали обнулити посилання.

Якщо у вас є об’єктний ланцюг A-> B-> C, після того, як A не буде доступний, A, B і C всі будуть придатні до вивезення сміття (якщо припустити, що більше нічого не стосується або B, або C). Наприклад, немає необхідності явно встановлювати посилання A-> B або B-> C на нуль, наприклад, і ніколи не було.

Крім цього, більшість часу питання насправді не виникає, адже насправді ви маєте справу з предметами в колекціях. Як правило, завжди потрібно думати про видалення об'єктів зі списків, карт тощо, викликаючи відповідний метод видалення ().

Випадок, коли раніше були поради щодо встановлення посилань на null, був спеціально у великій області, коли об'єкт, що займає пам'ять, перестав використовуватись частково через область застосування . Наприклад:

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

Обґрунтування тут полягало в тому, що оскільки obj все ще знаходиться в області застосування, то без явного обміну посиланням він не стає сміттєзбірним, поки не завершиться метод doSomethingElse () . І це рада, яка, ймовірно, більше не відповідає сучасним JVM : виявляється, що компілятор JIT може опрацювати, в який момент дана локальна посилання на об'єкт більше не використовується.


2
Локальні змінні можуть бути оптимізовані машиною. Змінні класу не можуть. Я бачив, що робочі потоки (у пулі потоків) не звільняють свої об'єкти стану після обробки запиту і таким чином закріплюють ці об'єкти в пам'яті, поки робочій нитці не буде надано нове завдання (яке негайно перезаписує старі об'єкти стану новими). З великими об'єктами стану та сотнями робочих ниток (думаю: великий http-сервер), це може бути величезна кількість пам'яті, яка зберігається та копіюється навколо, але більше ніколи не буде використовуватися знову.
Брайан Уайт

1
Я, мабуть, варто лише додати: рада, яку я даю вище, - це рада, яку слід дотримуватись загалом, якщо мати на увазі доглянуті бібліотеки, добре сприйнятий VM і т.д. робота закінчена, ну ... це помилка у згаданій бібліотеці пулових потоків, яку, можливо, можна було б обробити шляхом обнулення посилання на об'єкт, але однаково можна було б обійтись, використовуючи менш баггічну бібліотеку пулу потоків. Я не впевнений, що така помилка змінює загальні принципи дизайну.
Ніл Коффі

25

Ні, це не застаріла порада. Посилання на данглінг все ще залишається проблемою, особливо якщо ви, скажімо, реалізуєте контейнер масиву, що розширюється ( ArrayListабо подібне), використовуючи попередньо виділений масив. Елементи поза "логічним" розміром списку повинні бути скасовані, інакше вони не будуть звільнені.

Див. Ефективна Java 2-го видання, пункт 6: Усунення застарілих посилань на об’єкти.


Це мовна проблема чи проблема впровадження VM?
Пабло Санта-Крус

4
Це "семантичне" питання. В основному, оскільки ви попередньо виділили масив, VM це бачить. Він нічого не знає про "логічний" розмір вашого контейнера. Скажімо, у вас є ArrayList розміром 10, підкріплений масивом 16 елементів. ВМ не може знати, що елементи 10..15 насправді не використовуються; якщо ці слоти містять вміст, вони не будуть звільнені.
Кріс Єстер-Янг

а як бути з класами контейнерів? У складі об'єкта, де внутрішній об'єкт не виділений зовнішнім об'єктом.
сл

7
@sal Загальне правило полягає в тому, що якщо ви не можете посилатися на нього, воно збирається сміттям. Отже, якщо зовнішній об’єкт містить посилання на інший об’єкт, якщо вважати, що внутрішній об'єкт не має інших посилань, то встановлення одного і єдиного посилання зовнішнього об'єкта на нуль призведе до того, що весь об’єкт буде зібраний сміттям, включаючи його сиротні посилання.
ubermensch

2
У тому ж Пункті
ACV

10

Поля екземплярів, елементи масиву

Якщо є посилання на об'єкт, він не може бути зібраний сміттям. Тим більше, що той об'єкт (і весь графік за ним) великий, є лише одна посилання, яка зупиняє вивезення сміття, і ця довідка вже не потрібна, це нещасна ситуація.

Патологічні випадки - це об'єкт, який зберігає нестандартний екземпляр до всього дерева XML DOM, яке було використано для його налаштування, MBean, який не був зареєстрований, або єдине посилання на об'єкт із нерозподіленого веб-додатку, що не дозволяє завантажувати весь завантажувач класів. .

Тож якщо ви не впевнені, що об’єкт, який містить посилання, буде все-таки зібраним сміттям (або навіть тоді), вам слід анулювати все, що вам більше не потрібно.

Обсяг змінних:

Якщо ви розглядаєте можливість встановити локальну змінну до нуля до кінця області її дії, щоб її можна було відновити сміттєзбірником і позначити її як "непридатну з цього моменту", слід замість цього поставити її в більш обмежене поле .

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

стає

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

Довгі плоскі діапазони, як правило, також погані для розбірливості коду. Впровадження приватних методів для розбиття речей саме для цієї мети теж не є нечуваним.


Якщо ви посилаєтесь на чітку посилання, так, це правда, але не для всіх посилань. Слабкі посилання на Java можуть збирати сміття.
Джеймс Дрінкард

5

У обмежувальних середовищах пам'яті (наприклад, мобільні телефони) це може бути корисно. Встановивши null, objetc не потрібно чекати змінної, щоб вийти з області застосування, щоб бути gc'd.

Однак для повсякденного програмування це не повинно бути правилом, за винятком спеціальних випадків, таких як Кріс Єстер-Янг.


1

По-перше, це нічого не означає, що ви встановлюєте об'єкт null. Я пояснюю це нижче:

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

У сегменті коду вище ми створюємо ім'я змінної посилання на об'єкт list1з ArrayListоб'єкта , який зберігається в пам'яті. Так list1це посилання на цей об'єкт, і це не що інше, як змінна. А у другому рядку коду ми копіюємо посилання list1на list2. Отже, тепер повернусь до вашого питання, якщо я:

list1 = null;

це означає list1, що більше не посилається ні на який об’єкт, який зберігається в пам'яті, тому list2також не буде нічого посилатися. Тож якщо ви перевіряєте розмір list2:

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

Отож, тут з'являється концепція збору сміття, яка говорить: «Вам нічого не турбуватись про звільнення пам’яті, яка утримується об’єктом, я зроблю це, коли виявлю, що він більше не буде використовуватися в програмі, і JVM буде керувати мною».

Я сподіваюся, що це зрозуміла концепція.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.