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


89

Єдине, про що я знаю, PhantomReferenceце:

  • Якщо ви використовуєте його get()метод, він завжди повертається, nullа не об'єкт. Яка користь від цього?
  • Використовуючи PhantomReference, ви переконуєтесь, що об’єкт не можна відновити з finalizeметоду.

Але яка користь від цього поняття / класу?

Ви коли-небудь використовували це у будь-якому своєму проекті чи маєте якийсь приклад, де ми повинні цим користуватися?



Оскільки ви не можете отримати згаданий об'єкт PhantomReference, його повна неправильна назва: його слід було викликати FakeReferenceабо NonReference.
Pacerier

Ось anothre нитка з кодом: stackoverflow.com/q/43311825/632951
Pacerier

Відповіді:


47

Я використовував PhantomReferences у спрощеному, дуже спеціалізованому вигляді профайлера пам'яті для моніторингу створення та знищення об'єктів. Мені вони були потрібні для відстеження руйнувань. Але підхід застарів. (Це було написано в 2004 році для J2SE 1.4.) Професійні інструменти профілювання набагато потужніші та надійніші, і нові функції Java 5, такі як JMX або агенти та JVMTI, також можуть бути використані для цього.

PhantomReferences (завжди використовуються разом із чергою посилань) перевершують, finalizeщо має певні проблеми, тому їх слід уникати. В основному зробити об’єкти знову доступними. Цього можна уникнути за допомогою ідіоми охоронця фіналізатора (-> докладніше в "Ефективна Java"). Тож вони також є новим фіналом .

Крім того, PhantomReferenceс

дозволяють точно визначити, коли об’єкт було вилучено з пам'яті. Вони фактично є єдиним способом визначити це. Як правило, це не так корисно, але може стати в нагоді за певних дуже конкретних обставин, таких як маніпулювання великими зображеннями: якщо ви точно знаєте, що зображення має бути зібраним сміттям, ви можете почекати, поки воно насправді з’явиться, перед спробою завантажити наступне зображення , а отже, зробіть страшний OutOfMemoryError менш імовірним. (Процитовано від enicholas .)

І як psd писав першим, Roedy Green має хороший підсумок посилань .


21

Цілому нарізана таблиця пояснення , з Java Глосарію.

Що, звичайно, збігається з документацією PhantomReference :

Об'єкти-фантоми-еталони, які потрапляють у чергу після того, як колектор визначить, що їх референти можуть бути повернені в іншому випадку. Фантомні посилання найчастіше використовуються для планування дій передсмертельного очищення більш гнучко, ніж це можливо за допомогою механізму доопрацювання Java.

І останнє, але не менш важливе, усі криваві подробиці ( це добре прочитано ): Довідкові об’єкти Java (або Як я навчився перестати хвилюватися і полюбити OutOfMemoryError) .

Щасливого кодування. (Але, щоб відповісти на запитання, я коли-небудь використовував WeakReferences.)


До речі, питання про цю статтю. У розділі про PhantomReference він чітко посилається на об'єкти підключення через ці дві таблиці. Це означає, що з'єднання ніколи не стануть недосяжними (припускаючи, що сам екземпляр пулу ніколи не стає недосяжним). Тож відповідні PhantomReference ніколи не потраплять у чергу, чи не так? Або мені чогось не вистачає?
shrini1000

1
Ого, ця стаття від kdgregory заслуговує +10
Пейсерір

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. Ви також можете використовувати його для видалення агрегованих даних, які стають неактуальними після видалення об’єкта (але, мабуть, для цього існують кращі рішення). У прикладах часто згадується підключення до бази даних, яке слід закрити, але я не розумію, наскільки це така гарна ідея, оскільки ви не могли працювати з транзакціями. Фреймворк додатків забезпечить набагато краще рішення для цього.

Ви коли-небудь використовували це у будь-якому з своїх проектів, або у вас є якийсь приклад, де ми повинні використовувати це? Або ця концепція створена лише для точки зору співбесіди;)

Я використовую його здебільшого лише для реєстрації. Тож я можу простежити вилучені елементи і подивитися, як працює 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метод працює. Ви не можете, наприклад, написати a Phantom[Identity]HashMap.


IdentityHashMap <PhantomReference> насправді є одним із відповідних місць для IdentityHashMap. Зверніть увагу, що сильне посилання зберігається на PhantomReference, але не референта .

Ви насправді маєте на увазі, що для слабких посилань finalize може відтворити об'єкт? weakref.getможе повернутися null, а потім пізніше, він все ще може повернути об'єкт?
Pacerier

@Pacerier finalizeне відтворює об'єкт як такий. Це може зробити об'єкт знову важкодоступним після WeakReferenceповернення nullз getта в черзі. / (user166390: Як на карті, введеній у ціль посилання, так WeakHashMapсамо , як і не ідентифікаційна карта посилань, що добре.)
Tom Hawtin - tackline

1

якщо ви використовуєте метод get (), він завжди повертає null, а не об'єкт. [тоді яка користь від цього]

Корисними методами для виклику (а не get()) буде isEnqueued()або referenceQueue.remove(). Ви б викликали ці методи, щоб виконати якусь дію, яка повинна відбутися в останньому раунді збору сміття об’єкта.

Перший раз, коли об’єкт викликає свій finalize()метод, тому ви також можете туди помістити закриваючі гачки. Однак, як зазначали інші, існують, мабуть, більш надійні способи проведення прибирання або будь-яких інших дій, необхідних для проведення до і після вивезення сміття або, загальніше, після закінчення терміну експлуатації об’єкта.


1

Я знайшов ще одне практичне використання PhantomReferencesв класі LeakDetector Jetty.

Jetty використовує LeakDetectorклас, щоб визначити, чи клієнтський код отримує ресурс, але ніколи не випускає його, і LeakDetectorклас використовує PhantomReferencesдля цієї мети.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.