Вы когда-нибудь использовали PhantomReference в каком-либо проекте?

89

Я знаю только одно PhantomReference,

  • Если вы используете его get()метод, он всегда будет возвращать, nullа не объект. Какая от этого польза?
  • Используя PhantomReference, вы убедитесь, что объект нельзя воскресить из finalizeметода.

Но какова польза от этого понятия / класса?

Вы когда-нибудь использовали это в каком-либо из своих проектов или у вас есть какой-нибудь пример, где мы должны это использовать?

Ракеш Джуял
источник
Поскольку вы не можете получить упомянутый объект PhantomReference, это полное неправильное название: он должен был называться FakeReferenceили NonReference.
Pacerier
Вот еще один поток с кодом: stackoverflow.com/q/43311825/632951
Pacerier,

Ответы:

47

Я использовал PhantomReferences в упрощенном, очень специализированном профилировщике памяти для отслеживания создания и уничтожения объектов. Мне они были нужны, чтобы отслеживать разрушения. Но подход устарел. (Он был написан в 2004 году для J2SE 1.4.) Профессиональные инструменты профилирования намного мощнее и надежнее, и для этого также можно использовать новые функции Java 5, такие как JMX или агенты и JVMTI.

PhantomReferences (всегда используется вместе с очередью ссылок) лучше, finalizeчем с некоторыми проблемами, и поэтому их следует избегать. В основном делая объекты снова доступными. Этого можно было избежать с помощью идиомы-хранителя финализатора (-> подробнее см. «Эффективная Java»). Так они и в новой доработке .

Кроме того, PhantomReferences

позволяют точно определить, когда объект был удален из памяти. Фактически, это единственный способ определить это. Обычно это не так полезно, но может пригодиться в определенных очень специфических обстоятельствах, таких как манипулирование большими изображениями: если вы точно знаете, что изображение должно быть собрано в мусор, вы можете подождать, пока оно действительно не будет, прежде чем пытаться загрузить следующее изображение. , и, следовательно, уменьшить вероятность возникновения пугающей ошибки OutOfMemoryError. (Цит. Из Ениколаса .)

И, как первым написал psd , у Роеди Грина есть хорошее резюме ссылок .

Питер Кофлер
источник
21

Целом нарезанная таблица объяснение , из Java Глоссария.

Что, конечно, совпадает с документацией PhantomReference :

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

И последнее, но не менее важное, все кровавые подробности ( это хорошее чтение ): ссылочные объекты Java (или Как я научился перестать беспокоиться и полюбить OutOfMemoryError) .

Удачного кодирования. (Но чтобы ответить на вопрос, я когда-либо использовал только WeakReferences.)


источник
Кстати, вопрос по этой статье. В разделе PhantomReference он содержит сильные ссылки на объекты соединения через эти две таблицы. Это означает, что соединения никогда не станут недоступными (при условии, что сам экземпляр пула никогда не станет недоступным). Значит, соответствующие PhantomReferences никогда не будут помещены в очередь, верно? Или я что-то упускаю?
shrini1000
1
Вау, эта статья от kdgregory заслуживает +10
Pacerier
14

Отличное объяснение использования Phantom Reference:

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

Сергей Шевчик
источник
12

Я нашел практический и полезный вариант использования, PhantomReferenceкоторый есть org.apache.commons.io.FileCleaningTrackerв проекте commons-io. FileCleaningTrackerудалит физический файл, когда его объект-маркер будет удален.
Следует обратить внимание на Trackerкласс, расширяющий PhantomReferenceкласс.

Тан Хуэй Онн
источник
5

ЭТО ДОЛЖНО БЫТЬ УСТАРЕЛО В JAVA 9!
Используйте java.util.Cleanerвместо этого! (Или sun.misc.Cleanerна более старой JRE)

Исходный пост:


Я обнаружил, что использование PhantomReferences имеет почти такое же количество подводных камней, что и методы финализатора (но меньше проблем, если вы все сделаете правильно). Я написал небольшое решение (очень небольшую структуру для использования PhantomReferences) для Java 8. Оно позволяет использовать лямбда-выражения в качестве обратных вызовов, запускаемых после удаления объекта. Вы можете зарегистрировать обратные вызовы для внутренних ресурсов, которые должны быть закрыты. Благодаря этому я нашел решение, которое мне подходит, поскольку делает его гораздо более практичным.

https://github.com/claudemartin/java-cleanup

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

  class Foo implements Cleanup {
    //...  
    public Foo() { 
    //...    
      this.registerCleanup((value) -> {
        try {
          // 'value' is 'this.resource'
          value.close();
        } catch (Exception e) {
          logger.warning("closing resource failed", e);
        }
      }, this.resource);
    }

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

this.registerAutoClose(this.resource);

Чтобы ответить на ваши вопросы:

[тогда какая от этого польза]

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

Но какова польза от этого понятия / класса?

Необязательно делать что-либо с каким-либо эффектом, кроме отладки / ведения журнала. А может для статистики. Мне это больше похоже на службу уведомлений от GC. Вы также можете использовать его для удаления агрегированных данных, которые становятся неактуальными после удаления объекта (но, вероятно, для этого есть лучшие решения). В примерах часто упоминается, что соединения с базой данных должны быть закрыты, но я не понимаю, почему это такая хорошая идея, поскольку вы не можете работать с транзакциями. Фреймворк приложения предоставит для этого гораздо лучшее решение.

Вы когда-нибудь использовали это в каком-либо из своих проектов или у вас есть какой-нибудь пример, где мы должны это использовать? Или эта концепция сделана только для собеседования;)

Я использую его в основном только для регистрации. Так что я могу отследить удаленные элементы и посмотреть, как работает сборщик мусора и как его можно настроить. Я бы не стал запускать таким образом критический код. Если что-то нужно закрыть, это следует сделать с помощью оператора try-with-resource-statement. И я использую его в модульных тестах, чтобы убедиться, что у меня нет утечек памяти. Так же, как это делает jontejj. Но мое решение более общее.

Клод Мартин
источник
Да, как и мое решение «java-cleanup». Оба являются абстракциями, поэтому вам не нужно иметь с ними дело напрямую.
Клод Мартин
3

Я использовал PhantomReference в модульном тесте, чтобы убедиться, что тестируемый код не хранит ненужные ссылки на какой-либо объект. ( Исходный код )

import static com.google.common.base.Preconditions.checkNotNull;
import static org.fest.assertions.Assertions.assertThat;

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

import com.google.common.testing.GcFinalization;

/**
* Helps to test for memory leaks
*/
public final class MemoryTester
{
private MemoryTester()
{
}

/**
* A simple {@link PhantomReference} that can be used to assert that all references to it is
* gone.
*/
public static final class FinalizationAwareObject extends PhantomReference<Object>
{
private final WeakReference<Object> weakReference;

private FinalizationAwareObject(Object referent, ReferenceQueue<Object> referenceQueue)
{
super(checkNotNull(referent), referenceQueue);
weakReference = new WeakReference<Object>(referent, referenceQueue);
}

/**
* Runs a full {@link System#gc() GC} and asserts that the reference has been released
* afterwards
*/
public void assertThatNoMoreReferencesToReferentIsKept()
{
String leakedObjectDescription = String.valueOf(weakReference.get());
GcFinalization.awaitFullGc();
assertThat(isEnqueued()).as("Object: " + leakedObjectDescription + " was leaked").isTrue();
}
}

/**
* Creates a {@link FinalizationAwareObject} that will know if {@code referenceToKeepTrackOff}
* has been garbage collected. Call
* {@link FinalizationAwareObject#assertThatNoMoreReferencesToReferentIsKept()} when you expect
* all references to {@code referenceToKeepTrackOff} be gone.
*/
public static FinalizationAwareObject createFinalizationAwareObject(Object referenceToKeepTrackOff)
{
return new FinalizationAwareObject(referenceToKeepTrackOff, new ReferenceQueue<Object>());
}
}

И тест :

@Test
public void testThatHoldingOnToAnObjectIsTreatedAsALeak() throws Exception
{
    Object holdMeTight = new String("Hold-me-tight");
    FinalizationAwareObject finalizationAwareObject = MemoryTester.createFinalizationAwareObject(holdMeTight);
    try
    {
    finalizationAwareObject.assertThatNoMoreReferencesToReferentIsKept();
    fail("holdMeTight was held but memory leak tester did not discover it");
    }
    catch(AssertionError expected)
    {
    assertThat(expected).hasMessage("[Object: Hold-me-tight was leaked] expected:<[tru]e> but was:<[fals]e>");
    }
}
Джонтей
источник
2

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

Использование PhantomReferenceимеет тенденцию быть немного более навязчивым, потому что вы не можете притвориться, что getметод работает. Вы не можете, например, написать Phantom[Identity]HashMap.

Том Хотин - tackline
источник
IdentityHashMap <PhantomReference> на самом деле является одним из подходящих мест для IdentityHashMap. Обратите внимание, что сильная ссылка сохраняется на PhantomReference, но не на референт .
Вы действительно имеете в виду, что для слабых ссылок finalize может воссоздать obj? weakref.getможет вернуться null, а потом он все еще может вернуть obj?
Pacerier
@Pacerier finalizeне воссоздает объект как таковой. Это может сделать объект сильно достижимым снова после WeakReferenceвозвращения nullиз getи помещён. / (user166390: Как и на карте, привязанной к цели ссылки, как и в WeakHashMapслучае с картой идентичности ссылок, и это нормально.)
Том Хотин - tackline
1

если вы используете его метод get (), он всегда будет возвращать null, а не объект. [тогда какая от этого польза]

Полезными методами для вызова (а не get()) были бы isEnqueued()или referenceQueue.remove(). Вы могли бы вызвать эти методы, чтобы выполнить какое-либо действие, которое должно произойти в последнем цикле сборки мусора объекта.

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


источник
1

Я нашел другое практическое применение PhantomReferencesв классе LeakDetector Jetty.

Jetty использует LeakDetectorкласс, чтобы определить, получает ли клиентский код ресурс, но никогда не освобождает его, и LeakDetectorкласс использует PhantomReferencesдля этой цели.

Сахил Чабра
источник