Java: PhantomReference, ReferenceQueue и завершить

У меня есть PR, объект O, на который указывает PR, и RQ, настроенный для PR. У меня есть поток, который продолжает опрашивать RQ, и при первой ссылке, которую он находит в RQ, поток печатает время, в которое он его нашел, и завершает работу.

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

Вопрос: почему так происходит? Я'м с использованием Sun JDK 1.6.

Вот's код:

good case

public class MyGCPhantom 
{   
    public static void main(String[] args) throws InterruptedException 
    {       
        GCPhantomObject p = new GCPhantomObject();
        ReferenceQueue phantomQueue = new ReferenceQueue();
        PhantomReference pr = new PhantomReference(p, phantomQueue);      
        new GCPhantomThread(phantomQueue, "Phantom").start();
        p = null;

        System.gc();
    }
}

class GCPhantomObject
{   
    @Override
    protected void finalize()
    {
        //System.out.println("GCPhantom finalized " + System.currentTimeMillis());      
    }
}

class GCPhantomThread extends Thread
{
    private ReferenceQueue referenceQueue;
    private String name;

    GCPhantomThread(ReferenceQueue referenceQueue, String name)
    {
        this.referenceQueue = referenceQueue;
        this.name = name;
    }

    @Override
    public void run()
    {
        while(referenceQueue.poll() == null);       
        System.out.println(name + " found at " + System.currentTimeMillis());
    }
}

bad case

Просто раскомментируйте СОП вfinalize() изGCPhantomObject

 Walter Laan17 окт. 2012 г., 13:24
Не вызов super.finalize () может быть частью проблемы (хотя вы нев хорошем случае это тоже не так).

Ответы на вопрос(4)

Решение Вопроса

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

Я не вижу причин, по которым JVM по спецификации будет ставить в очередь ваши ссылки. Вы выполняете один прогон GC, а затем просто ждете, что что-то произойдет. Известно, что любой нетривиальный финализатор может воскресить объект, поэтому может потребоваться больше циклов GC, прежде чем он будет помещен в очередь. Я предлагаю добавить больше звонков GC.

Также обратите внимание, что ваше решение использоватьpoll вместоremove не рекомендуется. Вы должны использовать блокирующий вызов, чтобы предотвратить опрос занятых.

Для справки, это соответствующие определения из документации:

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

Объект является фантомно достижимым, если он не является ни сильно, ни мягко, ни слабо достижимым, он был завершен, и некоторая фантомная ссылка ссылается на него.

Завершенному объекту был автоматически вызван финализатор.

 shrini100017 окт. 2012 г., 14:29
Хм, работал с двумя вызовами gc без каких-либо задержек между ними. Спасибо за это объяснение.
 shrini100017 окт. 2012 г., 14:04
Очень хорошо, но это все еще нене отвечу на мой вопрос. Почему так происходит? Я вижу ту же самую проблему, даже если я заменяю эту СОП чем-то простымint y = 0; '
 shrini100017 окт. 2012 г., 14:09
Сначала я заменил poll () на remove ()Т помочь. Затем я заменил СОП с этим "ИНТ ...не сделалТ помочь. Когда я сделал finalize () тривиальным (пустое тело), он работал независимо от того, использовал ли я poll () или remove ().

пока объект не будет завершен. Вы делаете занятый цикл, так что это проблематично. Обратите внимание, что для завершения требуется как минимум два gcs.

 Shimi Bandiel21 окт. 2012 г., 14:13
Я думаю, это потому, что ты неу меня второй ГК после финализации
 shrini100017 окт. 2012 г., 13:34
Хорошо. Finalize () у меня тривиален. Так почему неPR появляется в RQ после запуска finalize ()?

м, как только мы проходим статью.

http://www.fasterj.com/articles/finalizer1.shtml

Чтобы объяснить это в двух словах: в случае нетривиального метода finalize (), даже если объектсобранное» при первом запуске GC он не удаляется физически. Это происходит при следующем запуске GC. Тот'почему объект PR появится в очереди во время второго GC.

размещенный здесь в моей системе, и он не работает даже после двух вызовов System.gc (). Он не завершается, даже если поместить System.gc () в цикл while класса GCPhantomThread.

Мне кажется, что проблема в том, что создаваемый вами объект никогда не помещается в ReferenceQueue, потому что он даже не фантомно доступен, когда работает GCPhantomThread. PhantomReference к объекту в методе main () выпадает из области видимости, поэтому, когда у вас запущен GCPhantomThread, объект даже не доступен для фантома. Согласно документам, для того, чтобы фантомная ссылка была поставлена в очередь, необходимы финализация и фантомная достижимость.

Это работает, когда я передаю фантомную ссылку на GCPhantomThread. На моей машине этот код всегда заканчивается:



    import java.lang.ref.PhantomReference;
    import java.lang.ref.ReferenceQueue;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;

    public class MyGCPhantom {
        public static void main(String[] args) throws InterruptedException {
            GCPhantomObject p = new GCPhantomObject();
            ReferenceQueue phantomQueue = new ReferenceQueue();
            PhantomReference pr = new PhantomReference(p, phantomQueue);
            new GCPhantomThread(pr, phantomQueue, "Phantom").start();
            p = null;
            pr = null;
            System.gc();
            System.out.println("main thread done ...");
        }
    }

    class GCPhantomObject {
        @Override
        protected void finalize() {
            System.out.println("GCPhantom finalized at " + System.nanoTime());
        }
    }

    class GCPhantomThread extends Thread {
        private ReferenceQueue referenceQueue;
        private String name;
        private PhantomReference pr;

        GCPhantomThread(PhantomReference pr, ReferenceQueue referenceQueue, String name) {
            this.referenceQueue = referenceQueue;
            this.name = name;
            this.pr = pr;
        }

        @Override
        public void run() {
            try {
                while (referenceQueue.remove(5000) == null) {
                    System.gc();
                }
                System.out.println(name + " found at " + System.nanoTime());
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

 shrini100022 окт. 2012 г., 09:48
Объект в main () доступен только фантомно, когда вызывается GC, потому что кадр стека еще не вытолкнут, поэтому для GC он видит только PR для этого объекта и, следовательно, собирает его. PR выходит за рамкипосле Возвращает main (), а не во время GC. В вашем случае вы использовали Sun JDK 6? Я последовательно получаю это поведение с этим.

Ваш ответ на вопрос