Czy kiedykolwiek korzystałeś z PhantomReference w jakimkolwiek projekcie?

89

Jedyne, o czym wiem, PhantomReferenceto

  • Jeśli użyjesz jego get()metody, zawsze zwróci, nulla nie obiekt. Jaki z tego pożytek?
  • Używając PhantomReference, upewniasz się, że obiekt nie może zostać wskrzeszony z finalizemetody.

Ale jaki jest pożytek z tego pojęcia / klasy?

Czy kiedykolwiek używałeś tego w którymś ze swoich projektów lub masz przykład, gdzie powinniśmy to wykorzystać?

Rakesh Juyal
źródło
Ponieważ nie możesz uzyskać odniesienia do obiektu PhantomReference, jest to kompletnie błędne określenie: powinno być nazwane FakeReferencelub NonReference.
Pacerier
Oto inny wątek z kodem: stackoverflow.com/q/43311825/632951
Pacerier

Odpowiedzi:

47

Użyłem go PhantomReferencew uproszczonym, bardzo wyspecjalizowanym rodzaju profilera pamięci do monitorowania tworzenia i niszczenia obiektów. Potrzebowałem ich, aby śledzić zniszczenia. Ale to podejście jest przestarzałe. (Został napisany w 2004 roku z myślą o J2SE 1.4.) Profesjonalne narzędzia do profilowania są znacznie wydajniejsze i bardziej niezawodne, a nowsze funkcje Java 5, takie jak JMX lub agenci i JVMTI, również mogą być używane do tego.

PhantomReferences (używane zawsze razem z kolejką referencyjną) są nadrzędne, finalizektóre powodują pewne problemy i dlatego należy ich unikać. Głównie sprawiając, że przedmioty stają się ponownie dostępne. Można tego uniknąć za pomocą idiomu strażnika finalizatora (-> więcej informacji w „Efektywna Java”). Są więc również nową finalizacją .

Ponadto PhantomReferences

pozwalają dokładnie określić, kiedy obiekt został usunięty z pamięci. W rzeczywistości są jedynym sposobem, aby to ustalić. Na ogół nie jest to przydatne, ale może się przydać w pewnych bardzo specyficznych okolicznościach, takich jak manipulowanie dużymi obrazami: jeśli wiesz na pewno, że obraz powinien zostać usunięty, możesz poczekać, aż faktycznie będzie, zanim spróbujesz załadować następny obraz , i tym samym zmniejsz prawdopodobieństwo przerażającego błędu OutOfMemoryError. (Cytat z enicholas .)

I jak psd napisał pierwszy, Roedy Green ma dobre podsumowanie odniesień .

Peter Kofler
źródło
21

Ogóle kostkę-up table wyjaśnienie , z Słowniczek Java.

Co oczywiście pokrywa się z dokumentacją PhantomReference :

Obiekty odniesienia fantomowe, które są umieszczane w kolejce po tym, jak kolektor ustali, że ich referencje mogą być w przeciwnym razie odzyskane. Odwołania fantomowe są najczęściej używane do planowania działań czyszczenia przed uśmierceniem w sposób bardziej elastyczny niż jest to możliwe w mechanizmie finalizacji Java.

I na koniec wszystkie krwawe szczegóły ( to dobra lektura ): Java Reference Objects (lub How I Learned to Stop Worrying and Love OutOfMemoryError) .

Miłego kodowania. (Ale żeby odpowiedzieć na pytanie, użyłem tylko WeakReferences.)


źródło
Przy okazji, pytanie dotyczące tego artykułu. W sekcji PhantomReference zachowuje silne odniesienie do obiektów połączeń za pośrednictwem tych dwóch tabel. Oznacza to, że połączenia nigdy nie staną się nieosiągalne (zakładając, że sama instancja puli nigdy nie stanie się nieosiągalna). Więc odpowiednie PhantomReferences nigdy nie zostaną dodane do kolejki, prawda? A może coś mi brakuje?
shrini1000
1
Wow, ten artykuł z kdgregory zasługuje na +10
Pacerier
14

Świetne wyjaśnienie użycia Phantom Reference:

Odniesienia fantomowe to bezpieczny sposób na sprawdzenie, czy obiekt został usunięty z pamięci. Weźmy na przykład pod uwagę aplikację, która obsługuje duże obrazy. Załóżmy, że chcemy załadować duży obraz do pamięci, gdy duży obraz jest już w pamięci i jest gotowy do zebrania śmieci. W takim przypadku chcemy poczekać, aż stary obraz zostanie zebrany, zanim załadujemy nowy. Tutaj odniesienie do fantomu jest elastyczną i bezpieczną opcją do wyboru. Odniesienie do starego obrazu zostanie umieszczone w kolejce w ReferenceQueue po sfinalizowaniu starego obiektu obrazu. Po otrzymaniu tego odniesienia możemy załadować nowy obraz do pamięci.

Sergii Szewczyk
źródło
12

Znalazłem praktyczny i użyteczny przypadek użycia, PhantomReferencektóry znajduje się org.apache.commons.io.FileCleaningTrackerw projekcie commons-io. FileCleaningTrackerusunie plik fizyczny, gdy jego obiekt znacznika zostanie usunięty.
Warto zwrócić uwagę na Trackerklasę, która rozszerza PhantomReferenceklasę.

Tan Hui Onn
źródło
5

W JAVA 9 TO POWINNO BYĆ PRZESTARZAŁE!
Użyj java.util.Cleanerzamiast tego! (Lub sun.misc.Cleanerw starszym JRE)

Oryginalny post:


Odkryłem, że użycie PhantomReferences ma prawie taką samą liczbę pułapek jak metody finalizujące (ale mniej problemów, gdy dobrze to zrobisz). Napisałem małe rozwiązanie (bardzo mały framework do używania PhantomReferences) dla Javy 8. Pozwala na użycie wyrażeń lambda jako wywołań zwrotnych uruchamianych po usunięciu obiektu. Możesz zarejestrować wywołania zwrotne zasobów wewnętrznych, które powinny zostać zamknięte. Dzięki temu znalazłem rozwiązanie, które działa dla mnie, ponieważ jest znacznie bardziej praktyczne.

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

Oto mały przykład pokazujący, jak rejestrowane jest wywołanie zwrotne:

  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);
    }

Jest jeszcze prostsza metoda automatycznego zamykania, działająca mniej więcej tak samo, jak powyżej:

this.registerAutoClose(this.resource);

Aby odpowiedzieć na Twoje pytania:

[w takim razie jaki z tego pożytek]

Nie możesz posprzątać czegoś, co nie istnieje. Ale mógł mieć zasoby, które nadal istnieją i wymagają oczyszczenia, aby można je było usunąć.

Ale jaki jest pożytek z tego pojęcia / klasy?

Niekoniecznie jest to robić coś innego niż debugowanie / rejestrowanie. A może dla statystyk. Widzę to bardziej jak usługę powiadomień z GC. Możesz również chcieć użyć go do usunięcia zagregowanych danych, które stają się nieistotne po usunięciu obiektu (ale prawdopodobnie są na to lepsze rozwiązania). Przykłady często wspominają o zamknięciu połączeń z bazą danych, ale nie widzę, jak to jest dobry pomysł, ponieważ nie można pracować z transakcjami. Framework aplikacji zapewni znacznie lepsze rozwiązanie tego problemu.

Czy kiedykolwiek użyłeś tego w którymś ze swoich projektów, czy masz jakiś przykład, gdzie powinniśmy to wykorzystać? A może jest to koncepcja stworzona tylko z punktu widzenia wywiadu;)

Używam go głównie do logowania. Mogę więc prześledzić usunięte elementy i zobaczyć, jak działa GC i można je dostosować. Nie uruchamiałbym w ten sposób żadnego krytycznego kodu. Jeśli coś wymaga zamknięcia, należy to zrobić w instrukcji try-with-resource. Używam go w testach jednostkowych, aby upewnić się, że nie mam żadnych wycieków pamięci. Tak samo jak robi to jontejj. Ale moje rozwiązanie jest nieco bardziej ogólne.

Claude Martin
źródło
Tak, tak jak moje rozwiązanie "java-cleanup". Obie są abstrakcjami, więc nie musisz zajmować się nimi bezpośrednio.
Claude Martin,
3

Użyłem PhantomReference w teście jednostkowym, aby sprawdzić, czy testowany kod nie zachował niepotrzebnych odwołań do jakiegoś obiektu. ( Oryginalny kod )

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>());
}
}

I test :

@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>");
    }
}
jontejj
źródło
2

Często używa się WeakReferencetam, gdzie PhantomReferencejest to bardziej odpowiednie. Pozwala to uniknąć pewnych problemów związanych z wskrzeszaniem obiektów po aWeakReference wyczyszczeniu / umieszczeniu w kolejce przez moduł odśmiecania pamięci. Zwykle różnica nie ma znaczenia, ponieważ ludzie nie bawią się w głupie robale.

Używanie PhantomReferencewydaje się być nieco bardziej uciążliwe, ponieważ nie można udawać, że getmetoda działa. Nie możesz na przykład napisać pliku Phantom[Identity]HashMap.

Tom Hawtin - haczyk
źródło
IdentityHashMap <PhantomReference> jest w rzeczywistości jednym z odpowiednich miejsc dla IdentityHashMap. Zwróć uwagę, że silne odniesienie zostało zachowane do PhantomReference, ale nie do odniesienia .
Czy faktycznie masz na myśli, że w przypadku słabych odniesień finalize może odtworzyć obiekt? weakref.getmoże zwrócić null, a później nadal jest w stanie zwrócić obiekt?
Pacerier
@Pacerier finalizenie odtwarza obiektu jako takiego. Może sprawić, że obiekt będzie ponownie silnie osiągalny po WeakReferencepowrocie nullz geti zostanie umieszczony w kolejce. / (user166390: Tak jak w przypadku mapy z kluczem do celu odniesienia, tak jak WeakHashMapnie jest to mapa tożsamości odniesień, co jest w porządku.)
Tom Hawtin - tackline
1

jeśli użyjesz jej metody get (), zawsze zwróci wartość null, a nie obiekt. [w takim razie jaki z tego pożytek]

Przydatnymi metodami do wywołania (zamiast get()) byłyby isEnqueued()lub referenceQueue.remove(). Możesz wywołać te metody, aby wykonać jakąś akcję, która musi mieć miejsce podczas ostatniej rundy czyszczenia pamięci obiektu.

Za pierwszym razem obiekt ma wywołaną finalize()metodę, więc możesz tam również umieścić haczyki zamykające. Jednak, jak stwierdzili inni, prawdopodobnie istnieją bardziej pewne sposoby wykonania czyszczenia lub jakiejkolwiek czynności, która musi zostać wykonana przed i po usunięciu elementów bezużytecznych lub, bardziej ogólnie, po zakończeniu eksploatacji obiektu.


źródło
1

Znalazłem inne praktyczne zastosowanie Jetty PhantomReferencesw klasie LeakDetector .

Jetty używa LeakDetectorklasy do wykrywania, czy kod klienta pozyskuje zasób, ale nigdy go nie zwalnia, a LeakDetectorklasa używa PhantomReferencesdo tego celu.

Sahil Chhabra
źródło