Referensi biasa yang dipakai sehari-hari hanyalah satu dari empat tingkat kekuatan yang dikenal JVM. Package java.lang.ref menyediakan soft, weak, dan phantom reference yang mengubah cara sebuah objek dipandang oleh garbage collection. Topik ini membedakan keempatnya pada tingkat pengenalan, lengkap dengan dua program yang dapat dijalankan untuk menyaksikan perbedaan perilakunya secara nyata.
Empat tingkat kekuatan referensi#
Referensi strong adalah referensi biasa, bentuk yang dipakai setiap hari, dan selama satu jalur referensi kuat dari GC root masih ada, objeknya tidak akan dikumpulkan. Referensi soft menunjuk objek yang boleh dilepas ketika memori menekan, sehingga lazim dipakai untuk cache yang lebih baik hilang daripada membuat heap habis. Referensi weak menunjuk objek yang dilepas pada GC berikutnya begitu tidak ada lagi referensi kuat yang menjangkaunya. Referensi phantom tidak pernah membuka objeknya sama sekali, melainkan memberi tahu lewat antrean bahwa objek sudah siap dibersihkan. Tangga kekuatannya digambarkan berikut.
Keempat tipe selain strong berupa kelas di java.lang.ref, sedangkan strong tidak punya kelas khusus karena bentuknya adalah default bahasa. Perbedaan perilaku terletak pada kapan GC boleh mengosongkan referensi itu, dan perbedaan inilah yang diuji dua program berikut.
Soft dan weak dalam satu program#
Program berikut membungkus dua objek dengan SoftReference dan WeakReference, lalu membuang referensi kuatnya dan memanggil System.gc().
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
public class UjiSoftVsWeak {
public static void main(String[] args) {
String dataA = new String("untuk soft");
String dataB = new String("untuk weak");
SoftReference<String> lembut = new SoftReference<>(dataA);
WeakReference<String> lemah = new WeakReference<>(dataB);
dataA = null;
dataB = null;
System.gc();
System.out.println("soft masih tersedia: " + (lembut.get() != null));
System.out.println("weak sudah dikosongkan: " + (lemah.get() == null));
}
}soft masih tersedia: true
weak sudah dikosongkan: truePada heap yang longgar seperti sandbox ini, referensi soft dibiarkan hidup sementara referensi weak dikosongkan pada GC yang sama. Kebijakan GC untuk soft reference adalah menunggu sampai memori benar-benar menekan sebelum melepasnya, dengan ambang yang dapat diatur lewat flag -XX:SoftRefLRUPolicyMSPerMB. Referensi weak tidak menunggu kebijakan apa pun, karena begitu objek hanya tersisa lewat referensi weak, GC berhak melepasnya seketika. Program di sini sengaja memakai objek kecil yang tidak menekan heap agar keluarannya dapat direproduksi, sebab pada heap yang mepet hasilnya bisa berbeda. Perilaku soft inilah yang membuatnya cocok untuk cache, sementara weak cocok untuk metadata yang tidak boleh memaksa objek tetap hidup.
Phantom dan ReferenceQueue#
Phantom reference bekerja dengan mekanisme yang berbeda dari semua tipe sebelumnya. Metode get() pada phantom reference selalu mengembalikan null oleh desain, sehingga isi objek tidak bisa dibaca lagi. Begitu objek menjadi phantom-reachable, referensinya dimasukkan ke ReferenceQueue yang terdaftar. Pemberitahuan di antrean inilah kesempatan melakukan pembersihan akhir, misalnya melepaskan memori native yang tidak dikelola GC.
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class UjiPhantomRef {
public static void main(String[] args) throws InterruptedException {
Object sesi = new Object();
ReferenceQueue<Object> antrean = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<>(sesi, antrean);
sesi = null;
System.gc();
boolean masukAntrean = false;
for (int i = 0; i < 50 && !masukAntrean; i++) {
masukAntrean = antrean.poll() == phantom;
if (!masukAntrean) {
Thread.sleep(10);
}
}
System.out.println("phantom masuk antrean setelah GC: " + masukAntrean);
System.out.println("get() pada phantom selalu: " + phantom.get());
}
}phantom masuk antrean setelah GC: true
get() pada phantom selalu: nullSetelah sesi diberi nilai null dan System.gc() dipanggil, loop menunggu pemberitahuan masuk. Pengecekan dibungkus loop dengan jeda pendek karena pemindahan ke antrean dikerjakan thread penanganan referensi JVM, bukan seketika pada baris yang sama dengan GC. Baris kedua keluaran menegaskan sifat phantom, yaitu isi objeknya tidak pernah dapat diambil kembali. Sifat ini membedakannya dari weak reference yang juga dapat terdaftar di ReferenceQueue, karena referensi weak masih dapat dibaca sampai saat pengosongannya, sedangkan phantom menutup pintu itu sepenuhnya agar urutan pembersihan tidak bisa salah. Mekanisme inilah fondasi kelas java.lang.ref.Cleaner yang menjadi pengganti resmi metode finalize() yang usang, yaitu mendaftarkan aksi pembersihan yang dijalankan setelah objek tidak terjangkau.
Kapan tiap tipe dipakai#
Tabel berikut merangkum kapan objek boleh dikumpulkan dan pemakaian yang lazim untuk tiap tipe.
| Tipe | Kapan objek boleh dikumpulkan | Pemakaian lazim |
|---|---|---|
| strong | tidak pernah selama masih dirujuk | default seluruh kode |
| soft | ketika memori menekan, sebelum OutOfMemoryError | cache yang boleh terlepas |
| weak | pada GC berikutnya | peta kanonik seperti WeakHashMap |
| phantom | setelah muncul di ReferenceQueue | pembersihan sumber daya non-heap |
Peta kanonik (canonical mapping) adalah pemakaian weak yang paling klasik, yaitu memakai objek sebagai kunci tanpa memaksa objek itu tetap hidup. Contohnya WeakHashMap, yang menghapus entrinya sendiri ketika kuncinya sudah tidak dirujuk di luar peta. Cache berbasis soft reference bekerja sebaliknya, yaitu menahan data selagi memori longgar dan melepasnya saat heap menekan. Phantom dan Cleaner mengambil peran pemakaman, yaitu menitipkan aksi terakhir untuk sumber daya yang berada di luar jangkauan GC seperti memori native. Dua pemakaian terakhir ini menuntut disiplin, karena referensi lemah tidak berguna bila salinan kuatnya masih tersebar di tempat lain.
Kesalahan umum#
- Memanggil
get()padaPhantomReferenceberharap mendapatkan objeknya. Kelas ini memang dirancang mengembalikannullselamanya, dan pembersihan dilakukan lewatReferenceQueue, bukan lewat pembacaan objek. - Menganggap soft reference terlepas pada momen tertentu. Waktu pelepasannya ditentukan tekanan memori, sehingga hanya boleh dipakai bila logika tetap benar saat datanya hilang.
- Menyimpan salinan kuat di tempat lain bersamaan dengan memakai weak atau soft. Satu salinan kuat saja sudah menjangkau objek dari GC root, sehingga referensi lemahnya tidak pernah dikosongkan dan fungsinya hilang.
- Memakai
finalize()untuk pembersihan akhir. Metode ini usang dan tidak menjanjikan waktu eksekusinya, sedangkanCleaneratau phantom reference memberi jalur yang setara tanpa kelemahannya.
Ringkasan#
- Strong adalah referensi biasa yang menjaga objek tetap hidup selama objek terjangkau dari GC root.
- Soft dilepas saat memori menekan dan cocok untuk cache, sedangkan weak dilepas pada GC berikutnya dan cocok untuk peta kanonik.
- Phantom tidak membuka isi objek dan memberi tahu lewat
ReferenceQueue, menjadi dasar kelasCleaner. - Satu salinan kuat yang tersisa sudah cukup membuat referensi lemah tidak pernah dilepas.
Referensi#
- Package java.lang.ref — Oracle — gambaran paket referensi dan kebijakan GC-nya.
- PhantomReference — Oracle
— dokumentasi sifat
get()yang selalunulldan peranReferenceQueue. - SoftReference — Oracle — kebijakan pelepasan soft reference saat memori menekan.