2015-09-10 2 views
3

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

Рассмотрим следующий пример:

List<String> list = new LinkedList<>(); 
// here we use the list, perhaps adding hundreds of items in it... 
// ...and now the work is done, the list is not needed anymore 
list.clear(); 
list = null; 

Из того, что я видел в реализациях, например, LinkedList или HashSet, метод clear() в основном только петли всех элементов данной коллекции, устанавливая все его элементы (в случае LinkedList также ссылок на последующие и предыдущие элементы) для null

Если бы я получил это право, установив list до null просто удаляет одну ссылку от list - учитывая, что это была единственная ссылка на нее, сборщик мусора в конечном итоге позаботится об этом. Я просто не знаю, сколько времени потребуется, пока в этом случае элементы списка не будут обработаны сборщиком мусора.

Итак, мой вопрос: сделайте последние две строки приведенного выше кода примера, чтобы помочь сборщику мусора работать более эффективно (т.е. собирать элементы списка раньше) или я просто сделаю свое приложение занятым «нерелевантными задачами» «?

ответ

8

Последние две строки не помогают.

  • После того, как переменная list выходит из области видимости *, если это последняя ссылка на связанный список, то список получает право на сбор мусора. Установка list на null сразу же не добавляет никакой ценности.

  • После того, как список станет подходящим для сбора мусора, поэтому выполните его элементы, если список содержит только ссылки на них. Очистка списка не требуется.

По большей части вы можете доверять сборщику мусора, чтобы выполнять свою работу, и не нужно «помогать» ему.

* Педантично говоря, это не сфера, которая контролирует вывоз мусора, но достижимости. Достижимость нелегко подытожить в одном предложении. См. this Q&A для объяснения этого различия.


Одно общее исключение из этого правила, если у вас есть код, который будет сохранять ссылки больше, чем они необходимы. Канонический пример этого - с слушателями.Если вы добавляете прослушиватель к некоторому компоненту, а позже этот прослушиватель больше не нужен, вам нужно явно его удалить. Если вы этого не сделаете, этот слушатель может препятствовать сбору мусора как самого, так и объектов, на которые он ссылается.

Скажем, я добавил слушателя к кнопке следующим образом:

button.addListener(event -> label.setText("clicked!")); 

Затем позже удаляется метка, но кнопка остается.

window.removeChild(label); 

Это проблема, потому что кнопка имеет ссылку на слушателя и слушатель имеет ссылку на этикетке. Метка не может быть собрана мусором, даже если она больше не видна на экране.

Это время, чтобы принять меры и получить хорошую сторону GC. Мне нужно помнить слушателя, когда я добавляю его ...

Listener listener = event -> label.setText("clicked!"); 
button.addListener(listener); 

... так что я могу удалить его, когда я закончу с меткой:

window.removeChild(label); 
button.removeListener(listener); 
+0

Это может быть полезно, чтобы добавить к вашему ответу комментария о анализе эвакуации Java 1.7 и далее. Мне вообще нравится ответ. Благодарю. – Sam

+0

@sam Что бы сказал это дополнение? Я не знаю, что случилось с анализом побега в Java 1.7 и дальше. –

+0

У меня не так много подробностей, поэтому я надеялся, что у вас есть что сказать. ;) Как я понимаю, во время компиляции и объекта можно определить, что срок службы вызова метода будет и будет выделен в стеке выполнения. В этом случае обнуление и очистка этого объекта становится еще более ненужным, так как всплытие стека будет очень быстро обрабатывать вещи. Google показывает это: http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html#zeroBasedCompressedOop – Sam

0

Я не верю, что clear() поможет в этом случае. GC будет удалять элементы, если на них больше нет ссылок, поэтому теоретически просто установка списка = null будет иметь тот же эффект. Вы не можете контролировать, когда GC будет вызван, поэтому, на мой взгляд, это не стоит беспокоиться, если у вас нет конкретных требований к ресурсам/производительности. Лично я все еще имел бы list = null;

Если вы хотите повторно использовать переменную списка, то, конечно, clear() - лучший вариант, а не создание нового объекта списка.

0

В Java объект является либо живым (доступным через ссылку, принадлежащим другому объекту), либо мертвым (недоступным владельцем справки каким-либо другим объектом). Объекты, которые доступны только от мертвых объектов, также считаются мертвыми и имеют право на сбор мусора.

Если ни один живой объект не имеет ссылки на вашу коллекцию, то он недоступен и имеет право на сбор мусора. Это также означает, что все элементы вашей коллекции (и любые другие вспомогательные объекты, которые она может создать): также недоступны, если какой-либо другой живой объект не имеет ссылки на них.

Следовательно, метод clear не имеет никакого эффекта, кроме как удалить ссылку с одного мертвого объекта на другой. Они будут собирать мусор в любом случае.

2

Это зависит от следующих факторов

  • как clear() реализуется
  • паттерны распределения для записей, находящихся в коллекции
  • сборщик мусора
  • ли могут быть и другие вещи, Держась сбор или подходы к нему (не относится к вашему примеру, но распространен в реальном мире)

Для примитивных, не относящихся к поколению трассирующих сборщиков мусора-сборщиков, ссылки на них означают дополнительную работу, не делая вещи намного проще на GC. Но очистка может по-прежнему помогать, если вы не можете гарантировать, что все ссылки на коллекцию будут своевременно отменены.

Для поколений ШС и особенно G1GC обнуления из ссылки внутри коллекции (или эталонного массива) может быть полезным при некоторых обстоятельствах путем уменьшения ссылки кросс-область.

Но это помогает, только если у вас есть шаблоны распределения, которые создают объекты в разных регионах и помещают их в коллекцию, живущую в другом регионе.И это также зависит от реализации clear(), обнуляющей эти ссылки, которая превращает очистку в операцию O (n), когда она часто может быть реализована как O (1).

Таким образом, для вашего конкретного примера ответа будет выглядеть следующим образом:

Если

  • вашего список долгоживущий
  • списков, созданных на этих кодовых путях составляют/держаться на значительная доля мусора, созданного вашей заявкой
  • Вы используете G1 или аналогичный многопользовательский коллектор
  • медленно накапливает объекты до того, арендованные (обычно это ставит их в различных регионах, создавая тем самым Перекрестные ссылки регион)
  • вы хотите торговать CPU-времени на очистку для уменьшения GC нагрузки
  • clear() реализация О (п) вместо O (1) , т.е. исключает все записи. OpenJDK 1,8 LinkedList делает это.

то может быть полезным для вызова clear() перед выпуском самой коллекции.

Таким образом, в лучшем случае это очень оптимизированная для рабочей нагрузки микро-оптимизация, которая должна применяться только после профилирования/мониторинга приложения в реальных условиях и определения того, что накладные расходы GC оправдывают дополнительную стоимость очистки.


Для справки, OpenJDK 1,8-х LinkedList::clear

/** 
* Removes all of the elements from this list. 
* The list will be empty after this call returns. 
*/ 
public void clear() { 
    // Clearing all of the links between nodes is "unnecessary", but: 
    // - helps a generational GC if the discarded nodes inhabit 
    // more than one generation 
    // - is sure to free memory even if there is a reachable Iterator 
    for (Node<E> x = first; x != null;) { 
     Node<E> next = x.next; 
     x.item = null; 
     x.next = null; 
     x.prev = null; 
     x = next; 
    } 
    first = last = null; 
    size = 0; 
    modCount++; 
} 
Смежные вопросы