Belum lama ini, saya mewawancarai beberapa kandidat yang mencari pekerjaan sebagai insinyur pengembangan Java senior. Saya sering mewawancarai mereka dan berkata, "Bisakah Anda memperkenalkan saya pada beberapa referensi lemah di Jawa?" Jika pewawancara berkata, "Apakah ini terkait dengan pengumpulan sampah?", Saya pada dasarnya akan puas, dan saya tidak akan melakukannya. Saya tidak mengharapkan jawabannya berupa uraian makalah yang sampai ke dasar permasalahan.
Namun, bertolak belakang dengan ekspektasi, saya terkejut saat mengetahui bahwa di antara hampir 20 kandidat dengan rata-rata pengalaman pembangunan 5 tahun dan latar belakang pendidikan tinggi, hanya dua orang yang mengetahui adanya referensi lemah, namun hanya satu dari dua orang tersebut yang benar-benar Pelajari tentang ini. Selama proses wawancara, saya juga mencoba menanyakan beberapa hal untuk melihat apakah ada yang tiba-tiba berkata "Jadi ini dia", tetapi hasilnya sangat mengecewakan. Saya mulai bertanya-tanya mengapa pengetahuan ini diabaikan begitu saja, referensi yang lemah adalah fitur yang sangat berguna, dan fitur ini diperkenalkan ketika Java 1.2 dirilis 7 tahun yang lalu.
Baiklah, saya tidak mengharapkan Anda menjadi ahli dalam referensi lemah setelah membaca artikel ini, tetapi menurut saya Anda setidaknya harus memahami apa itu referensi lemah, bagaimana menggunakannya, dan dalam skenario apa referensi tersebut digunakan. Karena ini adalah beberapa konsep yang tidak diketahui, saya akan menjelaskan secara singkat tiga pertanyaan sebelumnya.
Referensi Kuat
Referensi kuat adalah referensi yang sering kita gunakan, dan tertulis sebagai berikut:
Copy kode kodenya sebagai berikut:
StringBuffer buffer = StringBuffer baru();
Perintah di atas membuat objek StringBuffer dan menyimpan referensi (kuat) ke objek ini di buffer variabel. Ya, ini adalah operasi pediatrik (mohon maafkan saya jika mengatakan ini). Hal terpenting dari referensi yang kuat adalah dapat membuat referensi tersebut kuat, yang menentukan interaksinya dengan pemulung. Secara khusus, jika suatu objek dapat dijangkau melalui serangkaian tautan referensi yang kuat (Sangat dapat dijangkau), objek tersebut tidak akan didaur ulang. Inilah yang Anda perlukan jika Anda tidak ingin benda yang Anda kerjakan didaur ulang.
Tapi kutipan yang kuat sangatlah kuat
Dalam suatu program, jarang sekali menetapkan kelas menjadi non-extensible. Tentu saja, hal ini dapat dicapai dengan menandai kelas tersebut sebagai kelas final. Atau bisa lebih rumit, yaitu mengembalikan antarmuka (Interface) melalui metode pabrik yang berisi implementasi spesifik dalam jumlah yang tidak diketahui jumlahnya. Misalnya kita ingin menggunakan kelas bernama Widget, tetapi kelas ini tidak dapat diwarisi sehingga fungsi baru tidak dapat ditambahkan.
Namun apa yang harus kita lakukan jika ingin melacak informasi tambahan tentang objek Widget? Misalkan kita perlu mencatat nomor seri setiap objek, tetapi karena kelas Widget tidak berisi atribut ini dan tidak dapat diperluas, kita tidak dapat menambahkan atribut ini. Sebenarnya tidak ada masalah sama sekali. HashMap dapat menyelesaikan masalah di atas sepenuhnya.
Copy kode kodenya sebagai berikut:
serialNumberMap.put(widget, widgetSerialNumber);
Ini mungkin tampak baik-baik saja di permukaan, namun referensi kuat ke objek widget dapat menyebabkan masalah. Kami dapat yakin bahwa ketika nomor seri widget tidak lagi diperlukan, kami harus menghapus entri tersebut dari peta. Jika kami tidak menghapusnya, hal ini dapat menyebabkan kebocoran memori, atau kami dapat menghapus widget yang kami gunakan saat kami menghapusnya secara manual, yang dapat mengakibatkan hilangnya data valid. Faktanya, masalah ini sangat mirip. Ini adalah masalah yang sering dihadapi oleh bahasa tanpa mekanisme pengumpulan sampah saat mengelola memori. Namun masalah ini tidak perlu kita khawatirkan, karena kita menggunakan bahasa Java dengan mekanisme pengumpulan sampah.
Masalah lain yang mungkin disebabkan oleh referensi yang kuat adalah caching, terutama untuk file besar seperti gambar. Misalkan Anda memiliki program yang perlu memproses gambar yang disediakan oleh pengguna. Pendekatan yang umum adalah dengan menyimpan data gambar dalam cache, karena memuat gambar dari disk sangat mahal, dan pada saat yang sama kami juga ingin menghindari dua salinan data gambar yang sama. dalam memori secara bersamaan.
Tujuan dari caching adalah untuk mencegah kita memuat kembali file yang tidak diperlukan. Anda akan segera menemukan bahwa cache akan selalu berisi referensi ke data gambar di memori. Menggunakan referensi yang kuat akan memaksa data gambar untuk tetap berada di memori, yang mengharuskan Anda memutuskan kapan data gambar tidak lagi diperlukan dan menghapusnya secara manual dari cache sehingga dapat diambil kembali oleh pengumpul sampah. Jadi Anda sekali lagi terpaksa melakukan apa yang dilakukan pemulung dan secara manual memutuskan objek mana yang akan dibersihkan.
Referensi Lemah
Referensi yang lemah hanyalah referensi yang tidak begitu kuat dalam menyimpan objek di memori. Menggunakan WeakReference, pengumpul sampah akan membantu Anda memutuskan kapan objek yang direferensikan didaur ulang dan menghapus objek tersebut dari memori. Buat referensi lemah sebagai berikut:
Copy kode kodenya sebagai berikut:
eakReference<Widget>weakWidget = WeakReference<Widget>(widget);
Anda bisa mendapatkan objek Widget sebenarnya dengan menggunakanweakWidget.get(). Karena referensi yang lemah tidak dapat mencegah pengumpul sampah untuk mendaur ulangnya, Anda akan menemukan bahwa (bila tidak ada referensi kuat ke objek widget), null tiba-tiba dikembalikan saat menggunakan mendapatkan.
Cara termudah untuk memecahkan masalah pencatatan nomor urut widget di atas adalah dengan menggunakan kelas WeakHashMap bawaan Java. WeakHashMap hampir sama dengan HashMap, satu-satunya perbedaan adalah kuncinya (bukan nilai!!!) direferensikan oleh WeakReference. Ketika kunci WeakHashMap ditandai sebagai sampah, entri yang terkait dengan kunci ini akan dihapus secara otomatis. Hal ini untuk menghindari masalah penghapusan manual objek Widget yang tidak diperlukan di atas. WeakHashMap dapat dengan mudah dikonversi ke HashMap atau Map.
Antrean Referensi
Setelah objek referensi lemah mulai mengembalikan null, objek yang ditunjuk oleh referensi lemah ditandai sebagai sampah. Dan objek referensi yang lemah ini (bukan objek yang ditunjuknya) tidak ada gunanya. Biasanya beberapa pembersihan perlu dilakukan saat ini. Misalnya, WeakHashMap akan menghapus entri yang tidak berguna saat ini untuk menghindari penyimpanan referensi lemah yang tidak berarti dan tumbuh tanpa batas.
Antrean referensi memudahkan pelacakan referensi yang tidak diinginkan. Ketika Anda memasukkan objek ReferrenceQueue saat membuat WeakReference, ketika objek yang ditunjuk oleh referensi ditandai sebagai sampah, objek referensi akan secara otomatis ditambahkan ke antrian referensi. Selanjutnya, Anda dapat memproses antrian referensi yang masuk pada periode tertentu, seperti melakukan beberapa pekerjaan pembersihan untuk menangani objek referensi yang tidak berguna ini.
Empat macam referensi
Sebenarnya ada empat jenis referensi dengan kekuatan berbeda di Java, dari kuat hingga lemah, yaitu referensi kuat, referensi lunak, referensi lemah, dan referensi virtual. Bagian di atas memperkenalkan referensi kuat dan referensi lemah, dan berikut ini menjelaskan dua sisanya, referensi lunak dan referensi virtual.
Referensi Lembut
Referensi lunak pada dasarnya sama dengan referensi lemah, hanya saja ia memiliki kemampuan yang lebih kuat untuk mencegah periode pengumpulan sampah mendaur ulang objek yang ditunjuknya dibandingkan referensi lemah. Jika suatu objek dapat dijangkau oleh referensi yang lemah, objek tersebut akan dimusnahkan oleh pemulung pada siklus pengumpulan berikutnya. Namun jika soft reference dapat dijangkau, maka objek tersebut akan tersimpan lebih lama di memori. Pengumpul sampah hanya akan mengambil kembali objek yang dapat dijangkau oleh referensi lunak ini ketika memori tidak mencukupi.
Karena objek yang dapat dijangkau oleh referensi lunak akan tetap berada di memori lebih lama dibandingkan objek yang dapat dijangkau oleh referensi lemah, kita dapat menggunakan fitur ini untuk cache. Dengan cara ini, Anda dapat menghemat banyak hal. Pengumpul sampah akan peduli dengan jenis yang saat ini dapat dijangkau dan tingkat konsumsi memori untuk pemrosesan.
Referensi Hantu
Tidak seperti referensi lunak dan referensi lemah, objek yang ditunjuk oleh referensi virtual sangat rapuh, dan kita tidak bisa mendapatkan objek yang ditunjuk melalui metode get. Fungsinya hanya setelah objek yang ditunjuk didaur ulang, ditambahkan ke antrian referensi untuk mencatat bahwa objek yang ditunjuk oleh referensi telah dimusnahkan.
Ketika objek yang ditunjuk oleh referensi lemah dapat dijangkau oleh referensi lemah, referensi lemah tersebut ditambahkan ke antrian referensi. Operasi ini terjadi sebelum penghancuran objek atau pengumpulan sampah benar-benar terjadi. Secara teoritis, objek yang akan didaur ulang dapat dibangkitkan dengan metode destruktor yang tidak sesuai. Namun referensi lemah ini akan dimusnahkan. Referensi virtual ditambahkan ke antrian referensi hanya setelah objek yang ditunjuknya dihapus dari memori. Metode getnya terus mengembalikan null untuk mencegah objek yang hampir hancur yang ditunjuknya dibangkitkan kembali.
Ada dua skenario penggunaan utama untuk referensi virtual. Ini memungkinkan Anda mengetahui secara pasti kapan objek yang dirujuknya dihapus dari memori. Dan sebenarnya ini adalah satu-satunya cara di Jawa. Hal ini terutama berlaku ketika berhadapan dengan file besar seperti gambar. Saat Anda menentukan bahwa objek data gambar harus didaur ulang, Anda dapat menggunakan referensi virtual untuk menentukan apakah objek tersebut didaur ulang sebelum melanjutkan memuat gambar berikutnya. Dengan cara ini Anda dapat menghindari kesalahan kelebihan memori yang parah sebanyak mungkin.
Poin kedua adalah referensi virtual dapat menghindari banyak masalah selama pemusnahan. Metode finalize dapat menghidupkan kembali objek yang akan dihancurkan dengan membuat referensi kuat yang menunjuk ke objek tersebut. Namun, objek yang mengesampingkan metode finalisasi harus melalui dua siklus pengumpulan sampah terpisah jika ingin didaur ulang. Pada siklus pertama, suatu benda ditandai sebagai dapat didaur ulang dan kemudian dapat dimusnahkan. Namun karena masih kecil kemungkinan benda ini akan dibangkitkan kembali selama proses penghancuran. Dalam hal ini, pengumpul sampah perlu dijalankan kembali sebelum objek tersebut benar-benar dimusnahkan. Karena pemusnahan mungkin tidak terjadi pada waktu yang tepat, siklus pengumpulan sampah yang jumlahnya tidak dapat ditentukan harus dilalui sebelum pemusnahan suatu benda dapat dilakukan. Artinya, mungkin ada penundaan yang lama dalam pembersihan objek tersebut. Inilah sebabnya mengapa Anda masih mendapatkan kesalahan kehabisan memori yang mengganggu ketika sebagian besar tumpukan ditandai sebagai sampah.
Dengan menggunakan referensi virtual, situasi di atas akan terpecahkan. Ketika referensi virtual ditambahkan ke antrian referensi, Anda sama sekali tidak memiliki cara untuk mendapatkan objek yang dihancurkan. Karena saat ini objek tersebut telah dimusnahkan dari ingatan. Karena referensi virtual tidak dapat digunakan untuk membuat ulang objek yang ditunjuknya, objek tersebut akan dibersihkan pada siklus pertama pengumpulan sampah.
Jelasnya, metode finalisasi tidak disarankan untuk ditimpa. Karena referensi virtual jelas aman dan efisien, menghapus metode finalisasi dapat membuat mesin virtual menjadi lebih sederhana. Tentu saja, Anda juga dapat mengganti metode ini untuk mendapatkan lebih banyak manfaat. Itu semua tergantung pada pilihan pribadi.
Meringkaskan
Saya rasa melihat ini, banyak orang mulai mengeluh. Mengapa Anda berbicara tentang API lama dari sepuluh tahun terakhir? Nah, dari pengalaman saya, banyak programmer Java yang tidak mengetahui pengetahuan ini dengan baik -pemahaman yang mendalam diperlukan, dan saya berharap semua orang dapat memperoleh sesuatu dari artikel ini.