Безопасно ли иметь PhantomReference на `this`? - PullRequest
3 голосов
/ 02 июня 2019

У меня есть общий ресурс, для которого я хотел бы знать, сколько других объектов все еще используют этот ресурс. Для этого я хотел бы использовать PhantomReference s.

Поскольку ReferenceQueue s не отслеживают зарегистрированные для них ссылки ( источник , раздел "Уведомление"), моя идея заключалась в том, чтобы сохранить ссылку как поле отслеживаемого объекта:

class Foo {
    private PhantomReference<Foo> thisReference;

    public Foo(ReferenceQueue<Foo> queue) {
        thisReference = new PhantomReference<>(this, queue);
    }
}

Безопасно ли это, основываясь на поведении Java 9 (+) PhantomReference s, или возможно, что экземпляр собирается мусором без добавления ссылки в очередь?

В документации сказано:

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

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

Ответы [ 3 ]

3 голосов
/ 02 июня 2019

Я не думаю, что то, что вы делаете, сработает.

Предпосылки для объекта фантомно достижимого :

  • объект недостижим, легко доступен или слабо доступен,
  • объект был завершен, и
  • объект доступен из корня GC по крайней мере по одному пути с фантомной ссылкой.

В вашем случае, первые два предварительных условияудовлетворены, но третий нет.Если мы предположим, что this недоступен, это означает, что this.thisReference также недостижим.Это подразумевает, что Foo экземпляр PhantomReference экземпляра не будет иметь право быть помещенным в очередь.

(Однако, это "безопасно" в том смысле, что это не вызовет исключение или не приведет к аварийному завершению JVM, илиесть другие нежелательные побочные эффекты.)

2 голосов
/ 20 июня 2019

В документации к пакету содержится вводящая в заблуждение фраза: «Объект фантомно достижим , если он не является ни сильно, ни мягко, ни слабо достижимым, он завершен, и некоторые ссылки на фантомотносится к нему ».

То, что« некоторая фантомная ссылка »не подчеркивает тот факт, что сам эталонный объект должен быть достижимым, что указано в разделе Уведомление :

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

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

Обратите внимание, что определения мягкой и слабой достижимости лучше:

  • Объект является мягко достижимым , если он недостижимо, но может быть достигнуто путем обхода мягкой ссылки.
  • Объект является слабо достижимым , если он не является ни сильно, ни мягко достижимым, но может быть достигнут путем обхода слабой ссылки.…

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

Проблема с определениемФантомная достижимость аналогичным образом заключается в том, что фантомно достижимые объекты на самом деле недостижимы, так как PhantomReference переопределяет метод get(), чтобы всегда возвращать null, поэтому приложение не может достичь референта и, следовательно, не пройти через любой фантомдостижимые объекты.

Возможно, лучшим определением было бы

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

С этим определением было бы довольно ясно, что вашпример с собственной ссылкой не будет работать, как если бы он не работал с WeakReference или SoftReference.Обратите внимание, что когда в вашем классе нет выделенного finalize() метода, нет практической разницы между использованием WeakReference или PhantomReference.

. Кажется, что даже разработчики API не полностью понимали последствия,поскольку спецификация до Java 9 содержала правило:

В отличие от мягких и слабых ссылок, фантомные ссылки не очищаются сборщиком мусора автоматически, поскольку они ставятся в очередь.Объект, достижимый с помощью фантомных ссылок, будет оставаться таким до тех пор, пока все такие ссылки не будут очищены или сами не станут недоступными.

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

Начиная с Java 9, фантомные ссылки автоматически очищаются при постановке в очередьТаким образом, единственное практическое следствие перехода объекта из фантомно достижимого в недостижимого - это ссылка на фантом в ссылке.Что требует доступности эталонного объекта.

0 голосов
/ 16 июня 2019

Нет, сохранение ссылки на this не будет работать:

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class PhantomReferenceTest {
    private final PhantomReference<Object> thisReference;

    public PhantomReferenceTest(ReferenceQueue<Object> queue) {
        thisReference = new PhantomReference<>(this, queue);
    }

    public static void main(String[] args) throws InterruptedException {
        test(false);
        System.out.println("\nVerify that reference is enqueued if gc of thisReference is prevented:");
        test(true);
    }

    private static void test(boolean keepRefToRef) throws InterruptedException {
        ReferenceQueue<Object> queue = new ReferenceQueue<>();
        ReferenceQueue<Object> verifyQueue = new ReferenceQueue<>();

        PhantomReference<?> refToRef = null;
        PhantomReferenceTest obj = new PhantomReferenceTest(queue);
        PhantomReference<?> verifyReference = new PhantomReference<>(obj, verifyQueue);

        if (keepRefToRef) {
            // Verify that reference is enqueued if it is kept alive
            refToRef = obj.thisReference;
        }

        obj = null;

        System.gc();
        verifyQueue.remove();
        System.out.println("Object was collected");
        System.out.println("thisReference was enqueued: " + (queue.poll() != null));

        // Pretend to use refToRef to make sure gc cannot collect it
        if (refToRef != null) {
            refToRef.get();
        }
    }
}

Даже если это сработает, нет никаких гарантий, что оно продолжит работать и в будущем. Даже не гарантируется, что поле считается фантомно достижимым после включающего класса. В Java 12.0.1 противоположный случай:

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class EnqueuingOrder {
    private static class RefToField extends PhantomReference<Object> {
        public RefToField(Object obj, ReferenceQueue<Object> queue) {
            super(obj, queue);
        }
    }

    private final Object field;

    public EnqueuingOrder() {
        field = new Object();
    }

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Object> queue = new ReferenceQueue<>();

        EnqueuingOrder obj = new EnqueuingOrder();
        PhantomReference<?> refToObj = new PhantomReference<>(obj, queue);
        PhantomReference<?> refToField = new RefToField(obj.field, queue);
        obj = null;

        System.gc();

        System.out.println("First: " + queue.remove());
        System.out.println("Second: " + queue.remove());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...