Kapan Anda akan menggunakan WeakHashMap atau WeakReference?

163

Penggunaan referensi yang lemah adalah sesuatu yang belum pernah saya lihat implementasi, jadi saya mencoba mencari tahu apa use case untuk mereka dan bagaimana implementasi akan bekerja. Kapan Anda perlu menggunakan WeakHashMapatau WeakReferencedan bagaimana itu digunakan?

18Rbit
sumber
Mirip: Penggunaan WeakHashMap?
Basil Bourque

Jawaban:

96

Satu masalah dengan referensi kuat adalah caching, khususnya dengan struktur yang sangat besar seperti gambar. Misalkan Anda memiliki aplikasi yang harus bekerja dengan gambar yang disediakan pengguna, seperti alat desain situs web tempat saya bekerja. Tentu saja Anda ingin men-cache gambar ini, karena memuatnya dari disk sangat mahal dan Anda ingin menghindari kemungkinan memiliki dua salinan gambar (berpotensi raksasa) dalam memori sekaligus.

Karena cache gambar seharusnya mencegah kita memuat ulang gambar yang sebenarnya tidak kita perlukan, Anda akan segera menyadari bahwa cache harus selalu berisi referensi ke gambar apa pun yang sudah ada dalam memori. Namun, dengan referensi kuat yang biasa, referensi itu sendiri akan memaksa gambar untuk tetap berada dalam memori, yang mengharuskan Anda entah bagaimana menentukan kapan gambar tidak lagi diperlukan dalam memori dan menghapusnya dari cache, sehingga menjadi layak untuk pengumpulan sampah. Anda dipaksa untuk menduplikasi perilaku pengumpul sampah dan secara manual menentukan apakah suatu objek harus ada dalam memori.

Memahami Referensi Lemah , Ethan Nicholas

Jacob Krall
sumber
43
Bukankah SoftReferences lebih baik dalam hal ini, yaitu referensi yang hanya dikumpulkan ketika memori mulai habis.
JesperE
Saya agak bingung ... katakanlah saya memiliki cache Gambar SWT. Gambar SWT perlu DIHAPUS melalui metode dispose () untuk melepaskan Sumber Daya SO. Jika saya menggunakan WeakHashMap untuk menyimpannya, tunjukkan dengan tepat akankah GC membuang objek?
marcolopes
2
@marcolopes, GC akan menggunakan finalizer pada objek lain. Sepertinya SWT tidak suka ketika Anda melakukan itu, jadi saya tidak berpikir Anda dapat mengelola sumber daya sistem operasi dengan WeakHashMap.
Jacob Krall
@marcolopes, (Saya akan berasumsi bahwa GC Anda menjamin panggilan untuk menyelesaikan sebelum mengambil kembali memori.) Jika membuang dilakukan dalam penyelesai cache, semuanya baik-baik saja. Jika membuang adalah sesuatu yang harus Anda panggil secara manual, maka 1) perluas kelas dan masukkan ke dalam finalizer, atau 2) gunakan phantomreference untuk melacak dan menjalankan buang yang sesuai. Opsi 2 lebih baik (menghindari bug kebangkitan, dan memberi Anda kemampuan untuk menjalankan buang di utas lain), tetapi opsi 1 lebih mudah diimplementasikan tanpa helperclasses.
Pacerier
55

WeakReference melawan SoftReference

Satu perbedaan yang harus jelas adalah perbedaan antara a WeakReferencedan a SoftReference.

Pada dasarnya a WeakReferenceakan menjadi GC-d oleh JVM dengan penuh semangat, setelah objek yang direferensikan tidak memiliki referensi keras untuk itu. Sebuah SoftReferenced objek di sisi lain, akan cenderung ditinggalkan tentang oleh pengumpul sampah sampai benar-benar perlu untuk merebut kembali memori.

Tembolok tempat nilai disimpan di dalam WeakReferences akan sangat tidak berguna (dalam a WeakHashMap, itu adalah kunci yang direferensikan dengan lemah). SoftReferencesberguna untuk membungkus nilai-nilai ketika Anda ingin menerapkan cache yang dapat tumbuh dan menyusut dengan memori yang tersedia.

oxbow_lakes
sumber
4
"Tembolok tempat nilainya disimpan di dalam WeakReferences akan sangat tidak berguna," aku benar-benar tidak setuju.
Thomas Eding
5
@trinithis - erm, aku tidak benar-benar tahu harus berkata apa. Mengapa cache yang nilainya hilang saat Anda tidak mereferensikannya menjadi hal yang bermanfaat , tepatnya?
oxbow_lakes
4
Untuk sesuatu seperti memoisasi, cache yang secara longgar menyimpan nilai-nilai yang di-cache-nya bisa berguna.
Thomas Eding
5
@ Thomas Saya masih belum mengerti. Satu-satunya waktu cache tampaknya berguna adalah ketika tidak ada referensi lain untuk itu ... Jika Anda memiliki referensi untuk itu, untuk apa Anda membutuhkan cache?
Cruncher
2
@ Thomas, Softref memberitahu lingkungan "simpan ini sampai Anda tidak memiliki memori". Weakref memberitahu lingkungan "simpan ini sampai GC berjalan". Terus terang tidak ada kasus penggunaan untuk lemah, kecuali jika Anda men-debug / profiling GC itu sendiri. Jika Anda menginginkan cache yang peka terhadap memori, gunakan softref. Jika Anda tidak ingin cache, maka jangan cache! Di mana lemahref masuk?
Pacerier
30

Salah satu penggunaan umum WeakReferences dan WeakHashMaps khususnya adalah untuk menambahkan properti ke objek. Kadang-kadang Anda ingin menambahkan beberapa fungsionalitas atau data ke objek tetapi subclassing dan / atau komposisi bukanlah pilihan dalam hal itu hal yang jelas dilakukan adalah membuat hashmap yang menghubungkan objek yang ingin Anda rentangkan ke properti yang ingin Anda tambahkan . maka setiap kali Anda membutuhkan properti Anda bisa mencarinya di peta. Namun, jika objek yang Anda tambahkan properti cenderung dihancurkan dan dibuat banyak, Anda dapat berakhir dengan banyak objek lama di peta Anda yang menghabiskan banyak memori.

Jika Anda menggunakan WeakHashMapobjek, objek akan meninggalkan peta Anda segera setelah tidak lagi digunakan oleh program Anda, yang merupakan perilaku yang diinginkan.

Aku harus melakukan ini untuk menambahkan beberapa data untuk java.awt.Componentuntuk berkeliling perubahan dalam JRE antara 1.4.2 dan 1.5, saya bisa tetap dengan subclassing setiap komponen saya tertarik int ( JButton, JFrame, JPanel....) tapi ini jauh lebih mudah dengan kode lebih sedikit.

Lukas
sumber
1
bagaimana WeakHashMap tahu "mereka tidak lagi digunakan oleh sisa program Anda"?
Vinoth Kumar CM
2
Hasmap yang lemah menggunakan referensi yang lemah untuk kuncinya. Ketika sebuah objek hanya direferensikan melalui referensi lemah, pemulung akan 'memberi tahu' pemilik referensi lemah (dalam hal ini WeaHashMap). Saya akan membaca di WeakReferences dan ReferenceQueues di javadocs untuk memahami bagaimana benda-benda ini berinteraksi dengan pengumpul sampah.
Lukas
1
terima kasih Lukas, dapatkah Anda memberikan kode sederhana untuk deskripsi di atas?
mendidih
Dengan demikian, referensi lemah hanya masuk akal di Jawa karena API unik Java di mana beberapa kelas tidak dapat diperpanjang.
Pacerier
22

Kasus lain yang bermanfaat untuk WeakHashMapdan WeakReferenceadalah implementasi pendengar .

Ketika Anda membuat sesuatu yang ingin mendengarkan acara tertentu, biasanya Anda mendaftarkan pendengar, misalnya

manager.registerListener(myListenerImpl);

Jika managertoko menyimpan pendengar Anda dengan WeakReference, itu berarti Anda tidak perlu menghapus register misalnya dengan manager.removeListener(myListenerImpl)karena akan dihapus secara otomatis setelah pendengar Anda atau komponen Anda memegang pendengar menjadi tidak tersedia.

Tentu saja Anda masih dapat menghapus pendengar Anda secara manual, tetapi jika Anda tidak atau Anda melupakannya, itu tidak akan menyebabkan kebocoran memori, dan itu tidak akan mencegah pendengar Anda menjadi sampah yang dikumpulkan.

Dari mana datangnya WeakHashMapgambar itu?

Registri pendengar yang ingin menyimpan pendengar terdaftar sebagai WeakReferenceperlu koleksi untuk menyimpan referensi ini. Tidak ada WeakHashSetimplementasi di perpustakaan Java standar hanya WeakHashMaptetapi kita dapat dengan mudah menggunakan yang terakhir untuk "menerapkan" fungsi yang pertama:

Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

Dengan ini listenerSetuntuk mendaftarkan pendengar baru Anda hanya perlu menambahkannya ke set, dan bahkan jika itu tidak dihapus secara eksplisit, jika pendengar tidak lagi direferensikan, itu akan dihapus secara otomatis oleh JVM.

icza
sumber
10
Masalah dengan menggunakan lemahHashSets untuk daftar pendengar adalah bahwa contoh pendengar anonim yang dibuat dalam register () akan hilang dengan mudah, yang akan tak terduga oleh pengguna. Lebih aman untuk menyimpan referensi yang lebih kuat untuk pendengar dari manajer, dan mengandalkan penelepon untuk melakukan hal yang benar.
Gunanaresh
Untuk implementasi daftar pendengar: Semua pendengar yang terdaftar akan dikumpulkan / dihancurkan untuk tendangan GC berikutnya? Sebagai contoh, ketika memecat metode onSomethingHappened () semua pendengar, apa yang terjadi jika GC menendang?
blackkara
@icza, saya tidak percaya orang masih menjajakan mitos ini. Ini jawaban yang sepenuhnya salah. Anda mungkin juga mengatakan kasing lain yang berguna WeakHashMapadalah kapan pun Anda membutuhkan HashMapobjek. Jadi wow Anda tidak perlu secara manual melakukan hashmap.remove ever karena item secara otomatis dihapus setelah obj keluar dari ruang lingkup! Sungguh ajaib! Peretasan sihir jelek seperti itu adalah wajah yang lengkap .
Pacerier
3
@Pacerier: Saya telah mengikuti tautan Anda dari komentar lain di JavaScript hingga saat ini, dan masih belum mengerti mengapa penerapan pendengar dengan WeakMap adalah mitos. Sebagai contoh, jika klien WebSocket harus terikat dengan beberapa pendengar pada mereka melalui layanan registri, tampaknya logis untuk menyimpan objek soket sebagai kunci di WeakMap (untuk mencegah mereka menggantung di memori setelah koneksi ditutup, katakanlah, pada kesalahan) dan mampu untuk mengambil semua pendengar mereka jika perlu. Jadi, bisakah Anda nyatakan, apa yang salah dengan pendekatan itu?
Rusak Organik
1
@Pacerier Saya juga gagal memahami keberatan Anda. Dalam skenario Publikasi-Berlangganan atau Peristiwa, kumpulan referensi yang lemah masuk akal bagi saya. Objek berlangganan diizinkan untuk keluar dari ruang lingkup dan menuju pengumpulan sampah tanpa perlu berhenti berlangganan secara resmi. Proses berhenti berlangganan itu bisa sangat rumit jika objek pihak ketiga bertanggung jawab atas langganan awal tanpa pengetahuan objek berlangganan. Kumpulan WeakReferencesangat menyederhanakan basis kode dan menghindari bug yang tidak perlu terkait dengan gagal berhenti berlangganan. Kelemahan apa?
Basil Bourque
5

Posting blog ini menunjukkan penggunaan kedua kelas: Java: sinkronisasi pada ID . Penggunaannya berlangsung seperti ini:

private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider();

public void performTask(String resourceId) {
    IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId);
    synchronized (mutext) {
        // look up the resource and do something with it
    }
}

IdMutextProvider menyediakan objek berbasis id untuk disinkronkan. Persyaratannya adalah:

  • harus mengembalikan referensi ke objek yang sama untuk penggunaan bersamaan ID yang setara
  • harus mengembalikan objek yang berbeda untuk ID yang berbeda
  • tidak ada mekanisme rilis (objek tidak dikembalikan ke penyedia)
  • tidak boleh bocor (benda yang tidak digunakan memenuhi syarat untuk pengumpulan sampah)

Ini dicapai menggunakan peta jenis penyimpanan internal:

WeakHashMap<Mutex, WeakReference<Mutex>>

Objek adalah kunci dan nilai. Ketika tidak ada eksternal untuk peta yang memiliki referensi keras ke objek, itu bisa menjadi sampah yang dikumpulkan. Nilai-nilai di peta disimpan dengan referensi keras, sehingga nilainya harus dibungkus dalam Referensi Lemah untuk mencegah kebocoran memori. Hal terakhir ini tercakup dalam javadoc .

McDowell
sumber
3

Jika Anda misalnya ingin melacak semua objek yang dibuat dari kelas tertentu. Untuk tetap membiarkan objek-objek ini menjadi sampah yang dikumpulkan, Anda menyimpan daftar / peta referensi lemah ke objek bukan objek itu sendiri.

Sekarang jika seseorang bisa menjelaskan referensi hantu kepada saya, saya akan senang ...

JesperE
sumber
2
Satu penggunaan: PhantomReferences memungkinkan Anda untuk menentukan kapan objek dihapus dari memori. Mereka sebenarnya satu-satunya cara untuk menentukan itu. ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Jacob Krall
Sebenarnya itu tidak dihapus sampai Anda jelas-jelas menghapusnya. "Tidak seperti referensi lunak dan lemah, referensi hantu tidak secara otomatis dihapus oleh pengumpul sampah saat mereka enqueued. Objek yang dapat dijangkau melalui referensi hantu akan tetap begitu sampai semua referensi tersebut dihapus atau diri mereka sendiri menjadi tidak terjangkau."
jontro
@jontro, Tapi sudah selesai , semua anggota hilang. Secara efektif, itu adalah objek kosong. Lihat stackoverflow.com/q/7048767/632951
Pacerier
3

Seperti yang dinyatakan di atas, referensi lemah ditahan selama referensi kuat ada.

Contoh penggunaan adalah dengan menggunakan WeakReference di dalam pendengar, sehingga pendengar tidak lagi aktif setelah referensi utama ke objek target mereka hilang. Perhatikan bahwa ini tidak berarti WeakReference dihapus dari daftar pendengar, pembersihan masih diperlukan tetapi dapat dilakukan, misalnya, pada waktu yang dijadwalkan. Ini juga memiliki efek mencegah objek mendengarkan dari memegang referensi kuat dan akhirnya menjadi sumber memori kembung. Contoh: Ayunan komponen GUI merujuk model yang memiliki siklus hidup lebih lama dari jendela.

Saat bermain dengan pendengar seperti dijelaskan di atas, kami dengan cepat menyadari bahwa objek dikumpulkan "segera" dari sudut pandang pengguna.

Louis Jacomet
sumber
Terima kasih jawaban yang bermanfaat. Tetapi saya bertanya-tanya, dalam hal ini, apakah pendengar harus terdaftar (dirujuk) dengan kuat?
blackkara
Jawaban ini sepenuhnya salah. Elaborasi: stackoverflow.com/questions/154724/…
Pacerier
@Pacerier - untuk WeakReferenceskomentar Anda sepenuhnya salah!
2

Satu penggunaan dunia nyata yang saya miliki untuk WeakReferences adalah jika Anda memiliki objek tunggal yang sangat besar yang jarang digunakan. Anda tidak ingin menyimpannya di memori ketika tidak dibutuhkan; tetapi, jika utas lain membutuhkan objek yang sama, Anda juga tidak ingin keduanya dalam memori. Anda dapat menyimpan referensi yang lemah ke objek di suatu tempat, dan referensi keras dalam metode yang menggunakannya; ketika kedua metode selesai, objek akan dikumpulkan.


sumber
1
Ini adalah referensi lunak, bukan referensi lemah. Lihat stackoverflow.com/a/155492/632951
Pacerier
-1

Anda bisa menggunakan weakhashmap untuk mengimplementasikan caching bebas sumber daya untuk pembuatan objek yang luas.

tetapi perhatikan bahwa tidak diinginkan memiliki objek yang bisa berubah. saya menggunakannya untuk men-cache hasil query (yang membutuhkan sekitar 400 ms untuk dieksekusi) ke mesin pencari teks, yang jarang diperbarui.

Andreas Petersson
sumber
Anda sedang berbicara tentang referensi lunak, bukan referensi lemah. Lihat stackoverflow.com/a/155492/632951
Pacerier