Вызов System.gc()
только дает подсказку JVM, но не гарантирует, что произойдет фактическая сборка мусора.
Однако большая проблема с вашим ожиданием состоит в том, что сборка мусора не совпадает сфинализация.
Ссылаясь на документацию по Java 6, System.gc()
сообщает:
Запускает сборщик мусора.
Вызов метода gcпредполагает, что виртуальная машина Java затрачивает усилия на утилизацию неиспользуемых объектов, чтобы сделать доступной память, которую они занимают, для быстрого повторного использования.…
Сравнить с System.runFinalization()
:
Запускает методы завершения любых объектов, ожидающих завершения.
Вызов этого методапредполагает, что виртуальная машина Java затрачивает усилия на запуск методов финализации объектов, которые были обнаружены как отброшенные, но методы финализации которых еще не были запущены.…
Так что может быть «ожидающий окончание», соответственно.«Объекты, которые были обнаружены как отброшенные, но методы финализации которых еще не запущены».
К сожалению, документация Java 6 по finalize()
начинается с вводящего в заблуждение предложения:
Вызывается сборщиком мусора на объекте, когда сборщик мусора определяет, что больше нет ссылок на объект.
, тогда как сборка и финализация мусора - это две разные вещи, следовательно,finalize()
метод не , вызываемый сборщиком мусора.Но обратите внимание, что в следующей документации написано:
Язык программирования Java не гарантирует, какой поток вызовет метод finalize
для любого заданного объекта.
Так что, когда выскажем «порядок вывода результатов немного неубедителен», напомним, что мы говорим о многопоточности, поэтому в отсутствие дополнительной синхронизации порядок равен вне вашего контроля.
Спецификация языка Java даже гласит:
Язык программирования Java не определяет, как скоро будет вызван финализатор, за исключением того, чтобы сказать, что это произойдет до того, как хранилище дляобъект используется повторно.
и позже
Язык программирования Java не налагает упорядочения на вызовы метода finalize.Финализаторы могут вызываться в любом порядке или даже одновременно.
На практике сборщик мусора ставит в очередь только объекты, нуждающиеся в финализации, в то время как один или несколько потоков финализатора опрашивают очередь и выполняют методы finalize()
,Когда все потоки финализатора заняты выполнением определенных методов finalize()
, очередь объектов, нуждающихся в финализации, может увеличиваться произвольно долго.
Обратите внимание, что современные JVM содержат оптимизацию для тех классов, у которых нет выделенного метода finalize()
, т.е.наследовать метод от Object
или просто иметь пустой метод.Экземпляры этих классов, большинство всех объектов, пропускают этот этап завершения, и их пространство немедленно освобождается.
Так что, если вы добавили метод finalize()
просто для того, чтобы узнать, когда объект получает мусор, этоСамо присутствие этого finalize()
метода, который замедляет процесс восстановления памяти.
Так что лучше обратиться к версии JDK 11 finalize()
:
устарел. финализированМеханизм действия по своей сути проблематичен.Завершение может привести к проблемам с производительностью, тупикам и зависаниям.Ошибки в финализаторах могут привести к утечке ресурсов;нет возможности отменить финализацию, если она больше не нужна;и не указан порядок вызовов для завершения методов различных объектов.Кроме того, нет никаких гарантий относительно сроков завершения.Метод finalize может быть вызван для финализуемого объекта только после неопределенной задержки, если она вообще есть.Классы, экземпляры которых содержат ресурсы, не относящиеся к куче, должны предоставлять метод, позволяющий явное освобождение этих ресурсов, и они также должны реализовывать AutoCloseable, если это необходимо.Cleaner и PhantomReference предоставляют более гибкие и эффективные способы освобождения ресурсов, когда объект становится недоступным.
Таким образом, когда ваш объект не содержит ресурсов без памяти, следовательно, фактически непосле завершения вы можете использовать
class Test
{
int x = 100;
int y = 115;
}
class DelObj
{
public static void main(String[] arg)
{
Test t1 = new Test();
System.out.println("Values are "+t1.x+", "+t1.y+"\nObject refered by t1 is at location: "+t1);
WeakReference<Test> ref = new WeakReference<Test>(t1);
t1 = null; // dereferencing
System.gc(); // explicitly calling
if(ref.get() == null) System.out.println("Object deallocation is completed");
else System.out.println("Not collected");
Test t2= new Test();
System.out.println("Values are "+t2.x+", "+t2.y+"\nObject refered by t2 is at location: "+t2);
}
}
Вызов System.gc()
по-прежнему является лишь подсказкой, но вы обнаружите, что ваш объект впоследствии будет собран в большинстве практических случаев.Обратите внимание, что хеш-код, напечатанный для объектов, например Test@67f1fba0
, не имеет никакого отношения к ячейкам памяти;это цепкий миф.Паттерны, стоящие за адресами памяти объектов, часто не подходят для хэширования , кроме того, большинство современных JVM могут перемещать объекты в разные области памяти в течение срока их службы, тогда как хэш-код идентичности гарантированно останется прежним.