PhantomReference'ı hiç herhangi bir projede kullandınız mı?


90

Tek bildiğim şey PhantomReference,

  • get()Yöntemini kullanırsanız null, nesne değil daima geri dönecektir . Bunun ne yararı var?
  • Kullanarak PhantomReference, nesnenin finalizeyöntemden yeniden diriltilemeyeceğinden emin olursunuz .

Ama bu kavramın / sınıfın faydası nedir?

Bunu hiç bir projenizde kullandınız mı veya bunu kullanmamız gereken herhangi bir örneğiniz var mı?



Bir PhantomReference'ın belirtilen objesini alamadığınız için, bu tamamen yanlış bir isimdir: FakeReferenceveya çağrılması gerekirdi NonReference.
Pacerier

İşte kodlu bir başka konu: stackoverflow.com/q/43311825/632951
Pacerier

Yanıtlar:


47

Basit, çok özel bir bellek profilleyicisinde nesne oluşturma ve yok etmeyi izlemek için PhantomReferences kullandım . Yıkımı takip etmek için onlara ihtiyacım vardı. Ancak yaklaşımın modası geçmiş. (2004 yılında J2SE 1.4 hedeflenerek yazılmıştır.) Profesyonel profil oluşturma araçları çok daha güçlü ve güvenilirdir ve JMX veya aracılar ve JVMTI gibi daha yeni Java 5 özellikleri bunun için de kullanılabilir.

PhantomReferences (her zaman Referans kuyruğu ile birlikte kullanılır) finalize, bazı sorunları olanlardan daha üstündür ve bu nedenle kaçınılmalıdır. Esas olarak nesneleri tekrar ulaşılabilir kılmak. Bu, sonlandırıcı koruyucu deyimiyle önlenebilir (-> 'Etkili Java' bölümünde daha fazlasını okuyun). Yani onlar aynı zamanda yeni finalizedir .

Ayrıca, PhantomReferences

bir nesnenin bellekten tam olarak ne zaman kaldırıldığını belirlemenize olanak tanır. Aslında bunu belirlemenin tek yolu onlar. Bu genellikle o kadar kullanışlı değildir, ancak büyük resimleri işlemek gibi bazı çok özel durumlarda işe yarayabilir: Bir görüntünün çöp toplanması gerektiğinden eminseniz, bir sonraki görüntüyü yüklemeye çalışmadan önce gerçekten olana kadar bekleyebilirsiniz. ve bu nedenle dehşet verici OutOfMemoryError olasılığını azaltır. ( Enicholas'tan alıntılanmıştır .)

Ve psd ilk yazdığı, Roedy Yeşil bir sahiptir referansların iyi özetini .


21

Bir genel doğranmış yukarı tablo açıklama Java Sözlüğü'nden.

PhantomReference dokümantasyonu ile elbette çakışan :

Koleksiyoncu, referanslarının aksi takdirde geri alınabileceğini belirledikten sonra sıraya alınan hayali referans nesneleri. Hayali referanslar çoğunlukla, Java sonlandırma mekanizmasıyla mümkün olandan daha esnek bir şekilde ölüm öncesi temizleme eylemlerini planlamak için kullanılır.

Ve son olarak, tüm kanlı ayrıntılar ( bu iyi bir okuma ): Java Referans Nesneleri (veya Endişelenmeyi Durdurmayı ve OutOfMemoryError'ı Sevmeyi Nasıl Öğrendim) .

Mutlu kodlamalar. (Ancak soruyu cevaplamak için yalnızca WeakReferences'ı kullandım.)


Btw, o makale hakkında bir soru. PhantomReference ile ilgili bölümde, bu iki tablo üzerinden bağlantı nesnelerine güçlü bir referans veriyor. Bu, bağlantıların asla erişilemez olmayacağı anlamına gelir (havuz örneğinin kendisinin asla erişilemez hale gelmediğini varsayarsak). Yani ilgili PhantomReferences asla sıraya alınmayacak, değil mi? Yoksa bir şey mi kaçırıyorum?
shrini1000

1
Vay canına, kdgregory'deki bu makale +10'u hak ediyor
Pacerier

14

Fantom Referans kullanımının harika açıklaması :

Hayali referanslar, bir nesnenin bellekten kaldırıldığını bilmenin güvenli bir yoludur. Örneğin, büyük görüntülerle ilgilenen bir uygulama düşünün. Büyük görüntü zaten bellekte ve çöp için toplanmaya hazır olduğunda belleğe büyük bir görüntü yüklemek istediğimizi varsayalım. Böyle bir durumda, yenisini yüklemeden önce eski görüntünün toplanmasını beklemek istiyoruz. Burada, fantom referansı esnek ve güvenli bir seçim seçeneğidir. Eski görüntü nesnesi sonlandırıldığında, eski görüntünün referansı ReferenceQueue'da sıraya alınacaktır. Bu referansı aldıktan sonra yeni imajı hafızaya yükleyebiliriz.


12

Commons-io projesinde PhantomReferenceolan pratik ve kullanışlı bir kullanım durumu buldum org.apache.commons.io.FileCleaningTracker. FileCleaningTrackerişaret nesnesi çöp toplandığında fiziksel dosyayı siler.
Dikkat edilmesi gereken bir şey, Trackersınıfı genişleten PhantomReferencesınıftır.


5

BU JAVA 9 İLE ESKİ OLMALIDIR!
Onun java.util.Cleaneryerine kullanın ! (Veya sun.misc.Cleanerdaha eski JRE'de)

Orijinal gönderi:


PhantomReferences'ın kullanımının, sonlandırıcı yöntemlerle neredeyse aynı miktarda tuzağa sahip olduğunu buldum (ancak doğru anladığınızda daha az sorun var). Java 8 için küçük bir çözüm (PhantomReferences kullanmak için çok küçük bir çerçeve) yazdım. Nesne kaldırıldıktan sonra çalıştırılacak geri çağırmalar olarak lambda ifadelerinin kullanılmasına izin veriyor. Kapatılması gereken iç kaynaklar için geri aramaları kaydedebilirsiniz. Bununla benim için işe yarayan ve onu çok daha pratik hale getiren bir çözüm buldum.

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

Bir geri aramanın nasıl kaydedildiğini gösteren küçük bir örnek:

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

Ve sonra, yukarıdakinin aynısını yapan, otomatik kapatma için daha da basit bir yöntem var:

this.registerAutoClose(this.resource);

Sorularınızı cevaplamak için:

[o zaman ne işe yarar]

Var olmayan bir şeyi temizleyemezsin. Ancak hala var olan ve kaldırılabilmeleri için temizlenmesi gereken kaynaklara sahip olabilirdi.

Ama bu kavramın / sınıfın faydası nedir?

Hata ayıklama / günlüğe kaydetme dışında herhangi bir etkiye sahip bir şey yapmak zorunlu değildir. Ya da belki istatistikler için. Bunu daha çok GC'den bir bildirim hizmeti gibi görüyorum. Nesne kaldırıldıktan sonra alakasız hale gelen birleştirilmiş verileri kaldırmak için de kullanmak isteyebilirsiniz (ancak bunun için muhtemelen daha iyi çözümler vardır). Örnekler genellikle veritabanı bağlantılarının kapatılacağından bahsediyor, ancak bunun işlemlerle çalışamadığınız için nasıl iyi bir fikir olduğunu anlamıyorum. Bir uygulama çerçevesi bunun için çok daha iyi bir çözüm sağlayacaktır.

Bunu hiç bir projenizde kullandınız mı veya bunu kullanmamız gereken herhangi bir örneğiniz var mı? Yoksa bu kavram sadece görüşme açısından mı yapılmıştır;)

Çoğunlukla sadece kayıt için kullanıyorum. Böylece kaldırılan öğeleri izleyebilir ve GC'nin nasıl çalıştığını ve ince ayar yapılabileceğini görebilirim. Bu şekilde herhangi bir kritik kod çalıştırmam. Bir şeyin kapatılması gerekiyorsa, o zaman bir kaynakla dene ifadesinde yapılmalıdır. Bellek sızıntısı olmadığından emin olmak için bunu birim testlerinde kullanıyorum. Jontejj'in yaptığı gibi. Ama benim çözümüm biraz daha genel.


Evet, "java temizleme" çözümüm gibi. Her ikisi de soyutlamadır, bu yüzden onlarla doğrudan uğraşmanıza gerek yoktur.
Claude Martin

3

Bir birim testinde, test edilen kodun bir nesneye yönelik gereksiz referanslar tutmadığını doğrulamak için bir PhantomReference kullandım. ( Orijinal 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>());
}
}

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

2

Daha uygun olan WeakReferenceyerlerde kullanmak yaygındır PhantomReference. Bu, belirli bir süre sonra nesneleri yeniden diriltebilmenin bazı problemlerini önler.WeakReference çöp toplayıcı tarafından temizlendikten / sıraya . Genellikle farkın önemi yoktur çünkü insanlar aptal herifler oynamazlar.

Kullanmak PhantomReferencebiraz daha müdahaleci olma eğilimindedir çünkü getyöntemin çalıştığını iddia edemezsiniz . Örneğin, a yazamazsınız Phantom[Identity]HashMap.


IdentityHashMap <PhantomReference> aslında bir IdentityHashMap için uygun yerlerden biridir. O Not güçlü referans PhantomReference tutulması, referent ama .

Aslında zayıf referanslar için finalize'ın obj'i yeniden oluşturabileceğini mi söylüyorsunuz? weakref.getgeri dönebilir nullve daha sonra, yine de objeyi iade edebilir mi?
Pacerier

@Pacerier finalize, nesneyi bu şekilde yeniden oluşturmaz. Bu bir sonra kuvvetle ulaşılabilir tekrar nesneyi yapabilir WeakReferencegetiriler nullelde getve kuyruğa alınır. / (user166390: Referansın hedefine anahtarlanmış bir haritada olduğu WeakHashMapgibi, iyi olan referansların kimlik haritası değildir.)
Tom Hawtin - tackline

1

get () yöntemini kullanırsanız, nesne değil her zaman null döndürür. [o zaman ne işe yarar]

Kullanmak için yararlı yöntemler (yerine get()) isEnqueued()veya olacaktır referenceQueue.remove(). Nesnenin çöp toplama işleminin son turunda gerçekleşmesi gereken bazı eylemleri gerçekleştirmek için bu yöntemleri çağırırsınız.

İlk kez, nesnenin finalize()yönteminin çağrıldığı zamandır , böylece oraya da kapatma kancaları koyabilirsiniz. Bununla birlikte, diğerlerinin de belirttiği gibi, temizlemeyi gerçekleştirmenin muhtemelen daha kesin yolları vardır ya da çöp toplama öncesi ve sonrası veya daha genel olarak nesnenin kullanım ömrü sonunda yapılması gereken her türlü eylem vardır.


1

Ben başka pratik kullanım alanı bulmuş PhantomReferencesiçinde LeakDetector İskelesi'ne sınıfına.

Jetty LeakDetector, istemci kodunun bir kaynak alıp almadığını algılamak için LeakDetectorsınıfı kullanır, ancak onu asla serbest bırakmaz ve sınıf PhantomReferencesbu amaçla kullanır .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.