Panduan ke WeakHashMap di Java

1. Ikhtisar

Pada artikel ini, kita akan melihat WeakHashMap dari paket java.util .

Untuk memahami struktur data, kami akan menggunakannya di sini untuk meluncurkan implementasi cache sederhana. Namun, perlu diingat bahwa ini dimaksudkan untuk memahami cara kerja peta, dan membuat implementasi cache Anda sendiri hampir selalu merupakan ide yang buruk.

Sederhananya, WeakHashMap adalah implementasi berbasis hashtable dari antarmuka Map , dengan kunci berjenis WeakReference .

Entri di WeakHashMap akan secara otomatis dihapus ketika kuncinya tidak lagi digunakan secara biasa, artinya tidak ada Referensi tunggal yang mengarah ke kunci tersebut. Saat proses pengumpulan sampah (GC) membuang kunci, entri kunci tersebut secara efektif dihapus dari peta, sehingga kelas ini berperilaku agak berbeda dari implementasi Peta lainnya .

2. Referensi Kuat, Lembut, dan Lemah

Untuk memahami cara kerja WeakHashMap , kita perlu melihat kelas WeakReference - yang merupakan konstruksi dasar untuk kunci dalam implementasi WeakHashMap . Di Java, kami memiliki tiga jenis referensi utama, yang akan kami jelaskan di bagian berikut.

2.1. Referensi Kuat

Referensi yang kuat adalah jenis Referensi paling umum yang kami gunakan dalam pemrograman sehari-hari kami:

Integer prime = 1;

Variabel prima memiliki referensi yang kuat ke objek Integer dengan nilai 1. Objek yang memiliki referensi kuat yang menunjuk ke sana tidak memenuhi syarat untuk GC.

2.2. Referensi Lembut

Sederhananya, objek yang memiliki SoftReference yang mengarah ke sana tidak akan sampah dikumpulkan hingga JVM benar-benar membutuhkan memori.

Mari kita lihat bagaimana kita dapat membuat SoftReference di Java:

Integer prime = 1; SoftReference soft = new SoftReference(prime); prime = null;

The prime objek memiliki referensi yang kuat menunjuk ke sana.

Selanjutnya, kami membungkus referensi kuat utama menjadi referensi lunak. Setelah membuat referensi yang kuat tersebut menjadi null , objek utama memenuhi syarat untuk GC, tetapi akan dikumpulkan hanya jika JVM benar-benar membutuhkan memori.

2.3. Referensi yang Lemah

Objek yang hanya direferensikan oleh referensi yang lemah adalah sampah yang dikumpulkan dengan bersemangat; GC tidak akan menunggu sampai membutuhkan memori dalam kasus tersebut.

Kita dapat membuat WeakReference di Java dengan cara berikut:

Integer prime = 1; WeakReference soft = new WeakReference(prime); prime = null;

Saat kami membuat referensi utama menjadi null , objek utama akan dikumpulkan sampahnya di siklus GC berikutnya, karena tidak ada referensi kuat lainnya yang menunjuk ke sana.

Referensi dari tipe WeakReference digunakan sebagai kunci di WeakHashMap .

3. WeakHashMap sebagai Cache Memori yang Efisien

Katakanlah kita ingin membuat cache yang menyimpan objek gambar besar sebagai nilai, dan nama gambar sebagai kunci. Kami ingin memilih implementasi peta yang tepat untuk memecahkan masalah itu.

Menggunakan HashMap sederhana bukanlah pilihan yang baik karena objek nilai dapat menempati banyak memori. Terlebih lagi, mereka tidak akan pernah diambil kembali dari cache oleh proses GC, bahkan ketika mereka tidak lagi digunakan dalam aplikasi kita.

Idealnya, kami menginginkan implementasi Peta yang memungkinkan GC menghapus objek yang tidak digunakan secara otomatis. Ketika kunci objek gambar besar tidak digunakan di aplikasi kita di sembarang tempat, entri itu akan dihapus dari memori.

Untungnya, WeakHashMap memiliki karakteristik yang persis seperti ini. Mari kita uji WeakHashMap kami dan lihat bagaimana perilakunya:

WeakHashMap map = new WeakHashMap(); BigImage bigImage = new BigImage("image_id"); UniqueImageName imageName = new UniqueImageName("name_of_big_image"); map.put(imageName, bigImage); assertTrue(map.containsKey(imageName)); imageName = null; System.gc(); await().atMost(10, TimeUnit.SECONDS).until(map::isEmpty);

Kami membuat instance WeakHashMap yang akan menyimpan objek BigImage kami . Kami menempatkan objek BigImage sebagai nilai dan referensi objek imageName sebagai kunci. The imagename akan disimpan dalam peta sebagai WeakReference jenis.

Selanjutnya, kami menyetel referensi imageName menjadi null , oleh karena itu tidak ada lagi referensi yang mengarah ke objek bigImage . Perilaku default WeakHashMap adalah mengklaim kembali entri yang tidak memiliki referensi ke sana di GC berikutnya, jadi entri ini akan dihapus dari memori oleh proses GC berikutnya.

Kami memanggil System.gc () untuk memaksa JVM memicu proses GC. Setelah siklus GC, WeakHashMap kami akan kosong:

WeakHashMap map = new WeakHashMap(); BigImage bigImageFirst = new BigImage("foo"); UniqueImageName imageNameFirst = new UniqueImageName("name_of_big_image"); BigImage bigImageSecond = new BigImage("foo_2"); UniqueImageName imageNameSecond = new UniqueImageName("name_of_big_image_2"); map.put(imageNameFirst, bigImageFirst); map.put(imageNameSecond, bigImageSecond); assertTrue(map.containsKey(imageNameFirst)); assertTrue(map.containsKey(imageNameSecond)); imageNameFirst = null; System.gc(); await().atMost(10, TimeUnit.SECONDS) .until(() -> map.size() == 1); await().atMost(10, TimeUnit.SECONDS) .until(() -> map.containsKey(imageNameSecond));

Perhatikan bahwa hanya referensi imageNameFirst yang disetel ke null . The imageNameSecond referensi tetap tidak berubah. Setelah GC dipicu, peta hanya akan berisi satu entri - imageNameSecond .

4. Kesimpulan

Pada artikel ini, kami melihat jenis referensi di Java untuk memahami sepenuhnya bagaimana java.util. WeakHashMap berfungsi. Kami membuat cache sederhana yang memanfaatkan perilaku WeakHashMap dan menguji apakah berfungsi seperti yang kami harapkan.

Penerapan semua contoh dan cuplikan kode ini dapat ditemukan di proyek GitHub - yang merupakan proyek Maven, jadi semestinya mudah untuk mengimpor dan menjalankannya apa adanya.