Почему поведение сборщика мусора в 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 шт):

Автор решения: Barmaley Red Star

В идеале все кажется простым: сборщик мусора обходит все объекты и если объект нигде не имеет ссылок на другие объекты, то он освобождается и вызывается finalize(). Возможно так когда то и было, но не сейчас. Такой механизм работы сборщика мусора приводил бы к потере производительности и неоправданным затратам ресурсов.

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

Сначала все новые объекты попадают в т.н. eden (рай), размера рая лимитирован. Как только размер рая переполняется вызывается т.н. minor collection, который работает только внутри рая. После чистки объекты которые выжили перемещаются в следующую ступень взросления (aging), в котором сборка мусора происходит чуточку реже - и т.д. Чем дальше объект движется по ступеням взросления - тем он реже подвергается чистке. Объекты которые живут долго подвергаются чистке реже в ходе уже major collection. Схематично это можно изобразить таким графиком:

введите сюда описание изображения

На концепцию generations сверху накладываются еще несколько дополнительных ограничений/дополнений:

  1. Требования дополнительной памяти
  2. Производительность
  3. Приоретизация

Собственно говоря, вызов программиста - System.gc() - это просьба о повышении приоритета.

Update

Как правильно заметили в комментариях здесь описан принцип работы конкретного сборщика мусора, а именно для виртуальной машины Java Hotspot. Другие виртуальные машины (для тех кто в танке их много - неполный список здесь) могут использовать другие принципы.

→ Ссылка