Apakah Anda pernah menggunakan PhantomReference dalam proyek apa pun?

89

Satu-satunya hal yang saya tahu PhantomReferenceadalah,

  • Jika Anda menggunakan get()metodenya, itu akan selalu mengembalikan nulldan bukan objeknya. Apa gunanya itu?
  • Dengan menggunakan PhantomReference, Anda memastikan bahwa objek tidak dapat dibangkitkan dari finalizemetode.

Tapi apa gunanya konsep / kelas ini?

Pernahkah Anda menggunakan ini dalam salah satu proyek Anda atau apakah Anda memiliki contoh di mana kami harus menggunakan ini?

Rakesh Juyal
sumber
Karena Anda tidak bisa mendapatkan objek yang dirujuk dari PhantomReference, itu benar-benar keliru: Seharusnya dipanggil FakeReferenceatau NonReference.
Pacerier
Berikut utas lain dengan kode: stackoverflow.com/q/43311825/632951
Pacerier

Jawaban:

47

Saya menggunakan PhantomReferences dalam jenis profiler memori yang sangat khusus dan sederhana untuk memantau pembuatan dan penghancuran objek. Saya membutuhkan mereka untuk melacak kehancuran. Tapi pendekatannya sudah ketinggalan zaman. (Ditulis pada tahun 2004 dengan target J2SE 1.4.) Alat pembuatan profil profesional jauh lebih kuat dan andal dan fitur Java 5 yang lebih baru seperti JMX atau agen dan JVMTI dapat digunakan untuk itu juga.

PhantomReferences (selalu digunakan bersama dengan antrian Referensi) lebih unggul finalizeyang memiliki beberapa masalah dan oleh karena itu harus dihindari. Terutama membuat objek bisa dijangkau lagi. Ini bisa dihindari dengan idiom penjaga finalisator (-> baca lebih lanjut di 'Java Efektif'). Jadi mereka juga baru selesai .

Selanjutnya PhantomReferences

memungkinkan Anda untuk menentukan dengan tepat kapan suatu objek telah dihapus dari memori. Mereka sebenarnya satu-satunya cara untuk menentukannya. Ini umumnya tidak berguna, tetapi mungkin berguna dalam keadaan tertentu yang sangat spesifik seperti memanipulasi gambar besar: jika Anda tahu pasti bahwa gambar harus dikumpulkan dari sampah, Anda dapat menunggu sampai benar-benar sebelum mencoba memuat gambar berikutnya , dan karena itu memperkecil kemungkinan OutOfMemoryError yang ditakuti. (Dikutip dari enicholas .)

Dan seperti yang ditulis psd pertama kali, Roedy Green memiliki ringkasan referensi yang bagus .

Peter Kofler
sumber
21

Sebuah umum potong dadu-up table penjelasan , dari Glosarium Jawa.

Yang tentu saja sesuai dengan dokumentasi PhantomReference :

Objek referensi bayangan, yang diantrekan setelah kolektor menentukan bahwa referensi mereka dapat diklaim kembali. Referensi phantom paling sering digunakan untuk menjadwalkan tindakan pembersihan pra-mortem dengan cara yang lebih fleksibel daripada yang mungkin dilakukan dengan mekanisme finalisasi Java.

Dan yang tak kalah pentingnya, semua detail berdarah ( ini adalah bacaan yang bagus ): Objek Referensi Java (atau Cara Saya Belajar untuk Berhenti Khawatir dan Mencintai OutOfMemoryError) .

Selamat membuat kode. (Tetapi untuk menjawab pertanyaan itu, saya hanya pernah menggunakan WeakReferences.)


sumber
Btw, pertanyaan tentang artikel itu. Di bagian PhantomReference, dia menyimpan referensi yang kuat ke objek koneksi melalui dua tabel tersebut. Ini berarti bahwa koneksi tidak akan pernah menjadi tidak dapat dijangkau (dengan asumsi instance kumpulan itu sendiri tidak pernah menjadi tidak dapat dijangkau). Jadi PhantomReferences yang sesuai tidak akan pernah diantrekan, bukan? Atau apakah saya melewatkan sesuatu?
shrini1000
1
Wow, artikel dari kdgregory itu layak mendapatkan +10
Pacerier
14

Penjelasan bagus tentang penggunaan Referensi Phantom:

Referensi hantu adalah cara yang aman untuk mengetahui suatu objek telah dihapus dari memori. Misalnya, pertimbangkan aplikasi yang menangani gambar besar. Misalkan kita ingin memuat gambar besar ke memori ketika gambar besar sudah ada di memori yang siap untuk sampah dikumpulkan. Dalam kasus seperti itu, kami ingin menunggu hingga gambar lama dikumpulkan sebelum memuat yang baru. Di sini, referensi phantom fleksibel dan aman untuk dipilih. Referensi gambar lama akan diantrekan di ReferenceQueue setelah objek gambar lama diselesaikan. Setelah menerima referensi itu, kita dapat memuat gambar baru ke dalam memori.

Sergii Shevchyk
sumber
12

Saya menemukan kasus penggunaan yang praktis dan berguna PhantomReferenceyang ada org.apache.commons.io.FileCleaningTrackerdi proyek commons-io. FileCleaningTrackerakan menghapus file fisik saat objek penanda dikumpulkan sampah.
Sesuatu yang perlu diperhatikan adalah Trackerkelas yang memperluas PhantomReferencekelas.

Tan Hui Onn
sumber
5

INI HARUS DIBATALKAN DENGAN JAVA 9!
Gunakan java.util.Cleanersebagai gantinya! (Atau sun.misc.Cleanerdi JRE lama)

Posting asli:


Saya menemukan bahwa penggunaan PhantomReferences memiliki jumlah jebakan yang hampir sama dengan metode finalizer (tetapi masalah yang lebih sedikit setelah Anda melakukannya dengan benar). Saya telah menulis solusi kecil (kerangka kerja yang sangat kecil untuk menggunakan PhantomReferences) untuk Java 8. Ini memungkinkan untuk menggunakan ekspresi lambda sebagai callback untuk dijalankan setelah objek telah dihapus. Anda dapat mendaftarkan callback untuk sumber daya dalam yang harus ditutup. Dengan ini saya telah menemukan solusi yang bekerja untuk saya karena membuatnya jauh lebih praktis.

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

Berikut adalah contoh kecil untuk menunjukkan bagaimana panggilan balik didaftarkan:

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

Dan kemudian ada metode yang lebih sederhana untuk tutup otomatis, melakukan hal yang sama seperti di atas:

this.registerAutoClose(this.resource);

Untuk menjawab pertanyaan Anda:

[lalu apa gunanya]

Anda tidak dapat membersihkan sesuatu yang tidak ada. Tapi bisa saja sumber daya itu masih ada dan perlu dibersihkan agar bisa dihilangkan.

Tapi apa gunanya konsep / kelas ini?

Itu tidak harus melakukan apa pun dengan efek apa pun selain debugging / logging. Atau mungkin untuk statistik. Saya melihatnya lebih seperti layanan notifikasi dari GC. Anda juga dapat menggunakannya untuk menghapus data gabungan yang menjadi tidak relevan setelah objek dihapus (tetapi mungkin ada solusi yang lebih baik untuk itu). Contoh sering menyebutkan koneksi database untuk ditutup, tetapi saya tidak melihat bagaimana ini adalah ide yang bagus karena Anda tidak dapat bekerja dengan transaksi. Kerangka aplikasi akan memberikan solusi yang jauh lebih baik untuk itu.

Pernahkah Anda menggunakan ini di salah satu proyek Anda, atau apakah Anda punya contoh di mana kami harus menggunakan ini? Ataukah konsep ini dibuat hanya untuk sudut pandang wawancara;)

Saya menggunakannya sebagian besar hanya untuk logging. Jadi saya bisa melacak elemen yang dihapus dan melihat bagaimana GC bekerja dan bisa diubah. Saya tidak akan menjalankan kode penting dengan cara ini. Jika ada sesuatu yang perlu ditutup maka itu harus dilakukan dalam pernyataan coba-dengan-sumber daya. Dan saya menggunakannya dalam pengujian unit, untuk memastikan saya tidak memiliki kebocoran memori. Cara yang sama seperti jontejj melakukannya. Tetapi solusi saya sedikit lebih umum.

Claude Martin
sumber
Ya, seperti solusi saya "java-cleanup". Keduanya adalah abstraksi, jadi Anda tidak perlu membahasnya secara langsung.
Claude Martin
3

Saya menggunakan PhantomReference dalam pengujian unit untuk memverifikasi bahwa kode yang diuji tidak menyimpan referensi unnessecary ke beberapa objek. ( Kode asli )

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

Dan tesnya :

@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
sumber
2

Biasanya digunakan di WeakReferencetempat PhantomReferenceyang lebih tepat. Hal ini untuk menghindari masalah tertentu yaitu dapat menghidupkan kembali objek setelah WeakReferencedihapus / diantrekan oleh pengumpul sampah. Biasanya perbedaan itu tidak masalah karena orang tidak bermain-main dengan pengacau konyol.

Menggunakan PhantomReferencecenderung sedikit lebih mengganggu karena Anda tidak dapat berpura-pura bahwa getmetode tersebut berhasil. Anda tidak dapat, misalnya, menulis Phantom[Identity]HashMap.

Tom Hawtin - tackline
sumber
IdentityHashMap <PhantomReference> sebenarnya adalah salah satu tempat yang tepat untuk IdentityHashMap. Perhatikan bahwa referensi yang kuat disimpan ke PhantomReference, tetapi bukan referensi .
Apakah yang Anda maksud adalah bahwa untuk referensi yang lemah, penyelesaian dapat membuat ulang obj? weakref.getbisa kembali null, dan kemudian masih bisa mengembalikan obj?
Pacerier
@Pacerier finalizetidak membuat ulang objek seperti itu. Itu bisa membuat objek sangat mudah dijangkau lagi setelah WeakReferencekembali nulldari getdan diantrekan. / (user166390: Seperti pada peta yang dikunci pada target referensi, seperti WeakHashMaphalnya, bukan peta identitas referensi yang baik-baik saja.)
Tom Hawtin - tackline
1

jika Anda menggunakan metode get (), ia akan selalu mengembalikan null, dan bukan objeknya. [lalu apa gunanya]

Metode yang berguna untuk memanggil (daripada get()) akan menjadi isEnqueued()atau referenceQueue.remove(). Anda akan memanggil metode ini untuk melakukan beberapa tindakan yang perlu dilakukan pada putaran terakhir pengumpulan sampah objek.

Pertama kali sekitar adalah ketika objek memiliki finalize()metode yang dipanggil, jadi Anda bisa meletakkan kait penutup di sana juga. Namun, seperti yang dinyatakan orang lain, mungkin ada cara yang lebih pasti untuk melakukan pembersihan atau tindakan apa pun yang perlu dilakukan sebelum dan sesudah pengumpulan sampah atau, lebih umum lagi, pada akhir masa pakai objek.


sumber
1

Saya menemukan penggunaan praktis lain PhantomReferencesdi kelas LeakDetector dari Jetty.

Jetty menggunakan LeakDetectorkelas untuk mendeteksi apakah kode klien memperoleh sumber daya tetapi tidak pernah melepaskannya dan LeakDetectorkelas menggunakan PhantomReferencesuntuk tujuan ini.

Sahil Chhabra
sumber