Почему поведение сборщика мусора в Java считается непредсказуемым?
Интересует вопрос, почему поведение System.gc() считается непредсказуемым.
Вопрос с сертификации Java OCA.
Если взять в пример следующую ситуацию:
public class Bear {
protected void finalize() {
System.out.println("Roar!");
}
public static void main(String[] args) {
Bear bear = new Bear();
bear = null;
System.gc();
}
}
Roar! выводится в 100% случаев.
Однако правильным ответом считается утверждение, что Roar! может быть как выведен, так и не выведен.
До меня не доходит, почему вдруг gc может решить не собирать мусор в данной конкретной ситуации.
Ответы (1 шт):
В идеале все кажется простым: сборщик мусора обходит все объекты и если объект нигде не имеет ссылок на другие объекты, то он освобождается и вызывается finalize(). Возможно так когда то и было, но не сейчас. Такой механизм работы сборщика мусора приводил бы к потере производительности и неоправданным затратам ресурсов.
Поэтому сейчас все работает по другому - используется механизм разделения объектов на поколения (generations), согласно этому принципу чем дольше живет объект тем он дольше может прожить. В основе такого принципа лежит простое наблюдение, что большинство Java объектов живут очень короткое время и только малое их количество живет долго - соответственно спрашивается: а зачем тратить ресурсы на чистку малого количества объектов?
Сначала все новые объекты попадают в т.н. eden (рай), размера рая лимитирован. Как только размер рая переполняется вызывается т.н. minor collection, который работает только внутри рая. После чистки объекты которые выжили перемещаются в следующую ступень взросления (aging), в котором сборка мусора происходит чуточку реже - и т.д. Чем дальше объект движется по ступеням взросления - тем он реже подвергается чистке. Объекты которые живут долго подвергаются чистке реже в ходе уже major collection. Схематично это можно изобразить таким графиком:
На концепцию generations сверху накладываются еще несколько дополнительных ограничений/дополнений:
- Требования дополнительной памяти
- Производительность
- Приоретизация
Собственно говоря, вызов программиста - System.gc() - это просьба о повышении приоритета.
Update
Как правильно заметили в комментариях здесь описан принцип работы конкретного сборщика мусора, а именно для виртуальной машины Java Hotspot. Другие виртуальные машины (для тех кто в танке их много - неполный список здесь) могут использовать другие принципы.
