когда в этом сценарии будет вызываться finalize() для экземпляра моего класса?

Я знаю, что finalize() вызывается всякий раз, когда сборщик мусора собирает экземпляр класса. Однако я немного смущен при передаче экземпляра класса в другой поток через очередь.

Допустим, это скелет Thread1:

for(i=0; i<1000; i++) {
   Packet pkt = new Packet();  // instance of class
   pkt.id = i;
   thread2.queue.put(pkt);
}

Затем поток 2 удалит пакет из очереди и выполнит длительные операции. Получает ли этот второй поток «копию» пакета или это какая-то ссылка? Важно то, что, если это копия, finalize() экземпляра, созданного в потоке 1, может быть вызвана до того, как поток 2 завершит работу с пакетом. Если это по ссылке, мне гарантируется, что finalize() вызывается только один раз для информации в пакете.

Этот базовый пример может не отражать важность, но я сохраняю C-указатель (из JNI) в пакете, чтобы уничтожить часть памяти, когда закончу с объектом. Если он передается путем копирования, память может быть уничтожена до того, как второй поток завершит работу с ней. Если он передается по ссылке, то его следует уничтожить только после того, как GC увидит, что он больше не используется ОБОИМ потоком (мое желаемое поведение). Если этот последний сценарий не гарантирован, я не буду использовать finalize() и буду использовать что-то еще, но это будет более сложно.


person gnychis    schedule 21.07.2011    source источник
comment
Самое близкое сравнение C/++ для аргументов метода java состоит в том, что они являются указателем, переданным по значению. Так что это копия, но это копия только указателя. (Единственной операцией которого является разыменование.) Пока где-то в системе есть один указатель, указывающий на местоположение вашего объекта, это местоположение не будет собрано.   -  person Affe    schedule 21.07.2011
comment
Я настоятельно рекомендую вам добавить метод очистки в Packet для очистки указателя C, а не полагаться на метод финализации или даже использовать метод финализации.   -  person Michael Krussel    schedule 22.07.2011


Ответы (1)


Второй поток получает тот же фактический экземпляр объекта. Вы защищены от преждевременной доработки.

Он получает копию ссылки на объект, если вы хотите так думать.

Кроме того, finalize не обязательно запускается, когда сборщик мусора обнаруживает, что объект стал мусором — виртуальная машина может запустить его в любое время позже и фактически освободить память через некоторое время после этого. Вы действительно не можете полагаться на то, когда finalize будет запущен. Однако, поскольку вам важно знать, что finalize не будет вызываться до того, как второй поток завершит работу с объектом, это несущественно. Но стоит знать!

person Tom Anderson    schedule 21.07.2011
comment
большой! Большое спасибо за быстрый ответ. это сильно упрощает мне задачу :) - person gnychis; 21.07.2011
comment
Объект не будет собран до запуска finalize()! Объекты, которые переопределяют finalize, будут помещены в очередь финализатора, которая обрабатывается в потоке с низким приоритетом. Это еще одна причина, по которой вам следует избегать finalize(). - person Leonard Brünings; 21.07.2011
comment
@Damokles: Совершенно верно, конечно. Моя формулировка была небрежной, спасибо, что указали на это. Я перефразировал это, надеюсь, будет более правильным. - person Tom Anderson; 21.07.2011