Когда объект подходит для сбора мусора?

В приведенном ниже коде, учитывая, что был вызван amethod. В какой точке/строке находится объект, на который ссылается объект myObject, подходящий для коллекции мусора?

class Test {
  private Object classObject;

  public void amethod() {
    Object myObject = new Object();
    classObject = myObject;
    myObject = null;
  }
}

И если classObject или amethod имел модификатор доступа public, protected, default или static, повлияет ли он на то, в какой точке объект имеет право на сборку мусора? Если да, то как это повлияет?

  • Моя первая мысль заключается в том, что объект имеет право на сборку мусора, когда объект Test имеет право на сборку мусора.
  • Но опять же. Оптимизатор может знать, что classObject никогда не считывается, и в этом случае classObject = myObject; будет оптимизирован, а myObject = null; - это то, что он имеет право на сборку мусора.

Ответ 1

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

   classObject = myObject;

Вы назначили другую ссылку на тот же объект в куче. Итак, эта строка

   myObject = null;

Удаляется только одна ссылка. Чтобы сделать myObject кандидатом на сбор мусора, вы должны иметь

  classObject = null;

Ответ 2

Ваша идея о том, что частный объект может быть GC'd сразу, потому что ни один другой код не имеет доступа к нему, имеет некоторую тягу, но это будет связано с общей семантикой управления памятью Java. Например, если реализованный объект finalize и семантика Java четко определяют, когда объект имеет право на сбор мусора, этот метод финализатора должен быть вызван против спецификации.

Также обратите внимание, что объект, в свою очередь, может ссылаться на другие объекты с еще более сложными возможными результатами. Не говоря уже о том, что объект доступен в Reflection в любое время, и нет никакого смысла, чтобы наблюдаемое поле внезапно менялось на null, даже если ни один код не смог бы выполнить это назначение.

В заключение, есть много причин, по которым ваша идея оптимизации не будет работать в более широкой картине.

Ответ 3

В приведенном ниже коде, учитывая, что amethod был вызван. В какой точке/строке находится объект, на который ссылается myObject, подходящий для коллекции мусора?

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

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

Ответ 4

Из книги OCA Java SE 7

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

Ответ 5

На самом деле это точно определяется спецификацией языка Java, §12.6.1, Внедрение завершений:

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

Другой пример этого возникает, если значения в полях объектов хранятся в регистрах. Затем программа может обращаться к регистрам вместо объекта и никогда не обращаться к объекту еще раз. Это означает, что объект является мусором....

Но

... Обратите внимание, что такая оптимизация разрешена только в том случае, если ссылки находятся в стеке, а не в куче.

Например, рассмотрим шаблон Guardizer Guardizer:

   class Foo {
       private final Object finalizerGuardian = new Object() {
           protected void finalize() throws Throwable {
               /* finalize outer Foo object */
           }
       }
   } 
     

Защитник-финализатор заставляет super.finalize вызываться, если подкласс переопределяет finalize и явно не вызывает super.finalize.

     

Если эти оптимизации разрешены для ссылок, которые хранятся в куче, тогда компилятор Java может обнаружить, что поле finalizerGuardian никогда не читается, обнуляет его, немедленно собирает объект и вызывает окончательный финализатор раньше. Это противоречит намерению: программист, вероятно, захотел назвать финализатор Foo, когда экземпляр Foo стал недоступен. Таким образом, такое преобразование не является законным: внутренний объект класса должен быть доступен до тех пор, пока объект внешнего класса достигнут.

Этот пример может быть применен 1:1 к вашему примеру, если объект ссылается на поле экземпляра classObject, он не может получить мусор, собранный раньше, чем экземпляр Test, содержащий ссылку.

Обратите внимание, однако, что агрессивная оптимизация, упомянутая в спецификации, по-прежнему разрешена, когда применяется к коду с использованием экземпляра Test. Возможно, существует более ранняя, чем ожидалось, коллекция, если оба экземпляра Test и ссылочный объект собираются вместе. В этом случае применяется следующий аспект, указанный в §12.6:

Язык программирования Java не требует упорядочения при завершении вызовов метода. Финализаторы могут вызываться в любом порядке или даже одновременно.

Таким образом, вполне возможно, что экземпляр Test собирается раньше объекта, на который ссылается classObject, тогда как финализатор "внутренних" объектов вызывается ранее. Единственное, что гарантировано, заключается в том, что при выполнении финализатора внутренних объектов внешний объект недоступен (или имеет ожидающую или параллельную доработку). Поскольку в вашем примере нет ни одного нетривиального финализатора, это не имеет никакого значения...

Ответ 6

Поскольку вы держите myObject в classObject (ссылка поддерживается), он (объект в памяти, на который ссылается через classObject) не будет доступен для коллекции Garbage, пока экземпляр Test не будет освобожден/разгружен.

Ответ 7

Никакой объект не имеет права на сборку мусора здесь, потому что вы создаете две ссылки для одного и того же объекта, и вы даете нуль только одной ссылке, но другая ссылка все еще указывает на ваш объект