Saya telah membaca banyak pertanyaan tentang Java pemula finalize()
dan merasa agak membingungkan bahwa tidak ada yang benar-benar menjelaskan bahwa menyelesaikan () adalah cara yang tidak dapat diandalkan untuk membersihkan sumber daya. Saya melihat seseorang berkomentar bahwa mereka menggunakannya untuk membersihkan Connections, yang benar-benar menakutkan karena satu-satunya cara untuk sedekat dengan jaminan bahwa Koneksi ditutup adalah dengan mengimplementasikan try (catch) akhirnya.
Saya tidak bersekolah di CS, tetapi saya telah memprogram di Jawa secara profesional selama hampir satu dekade sekarang dan saya belum pernah melihat ada yang menerapkan finalize()
sistem produksi. Ini masih tidak berarti bahwa itu tidak memiliki kegunaannya, atau bahwa orang yang telah bekerja dengan saya telah melakukannya dengan benar.
Jadi pertanyaan saya adalah, kasus penggunaan apa yang ada untuk implementasi finalize()
yang tidak dapat ditangani dengan lebih andal melalui proses lain atau sintaksis dalam bahasa?
Harap berikan skenario khusus atau pengalaman Anda, cukup mengulangi buku teks Java, atau menyelesaikan penggunaan yang dimaksudkan tidak cukup, karena bukan maksud pertanyaan ini.
finalize()
. Namun, kode pustaka platform , sepertiSocketInputStream
, yang mengelola sumber daya asli atas nama penelepon, melakukan hal itu untuk meminimalkan risiko kebocoran sumber daya (atau menggunakan mekanisme setara, sepertiPhantomReference
, yang ditambahkan kemudian.) Jadi ekosistem membutuhkannya , meskipun 99,9999% pengembang tidak akan pernah menulis satu pun.Jawaban:
Anda bisa menggunakannya sebagai backstop untuk objek yang memegang sumber daya eksternal (soket, file, dll). Menerapkan
close()
metode dan dokumen yang perlu dipanggil.Terapkan
finalize()
untuk melakukanclose()
pemrosesan jika Anda mendeteksinya belum dilakukan. Mungkin dengan sesuatu yang dibuang untukstderr
menunjukkan bahwa Anda membersihkan setelah penelepon kereta.Ini memberikan keamanan ekstra dalam situasi yang luar biasa / kereta. Tidak setiap penelepon akan melakukan
try {} finally {}
hal yang benar setiap saat. Sayangnya, tetapi berlaku di sebagian besar lingkungan.Saya setuju bahwa ini jarang dibutuhkan. Dan seperti yang ditunjukkan komentator, ia hadir dengan overhead GC. Hanya gunakan jika Anda membutuhkan keamanan "sabuk dan suspender" di aplikasi yang sudah berjalan lama.
Saya melihat bahwa pada Java 9,
Object.finalize()
sudah usang! Mereka mengarahkan kita kejava.lang.ref.Cleaner
danjava.lang.ref.PhantomReference
sebagai alternatif.sumber
finalize()
adalah petunjuk bagi JVM bahwa mungkin baik untuk mengeksekusi kode Anda pada waktu yang tidak ditentukan. Ini bagus ketika Anda ingin kode gagal dijalankan secara misterius.Melakukan sesuatu yang signifikan dalam finalizer (pada dasarnya apa pun kecuali logging) juga baik dalam tiga situasi:
Jika Anda merasa Anda perlu menyelesaikan (), kadang-kadang yang Anda inginkan adalah referensi hantu (yang dalam contoh yang diberikan dapat menyimpan referensi keras ke koneksi yang digunakan oleh referensi dan, dan menutupnya setelah referensi hantu telah antri). Ini juga memiliki properti yang mungkin tidak pernah dijalankan secara misterius, tetapi setidaknya ia tidak dapat memanggil metode atau menghidupkan kembali objek yang telah diselesaikan. Jadi itu tepat untuk situasi di mana Anda tidak perlu benar-benar menutup koneksi itu dengan bersih, tetapi Anda cukup ingin, dan klien dari kelas Anda tidak dapat atau tidak akan menyebut diri mereka sendiri (yang sebenarnya cukup adil - apa' Apakah gunanya memiliki pengumpul sampah sama sekali jika Anda mendesain antarmuka yang memerlukan tindakan khusus sebelum pengumpulan? Itu hanya menempatkan kita kembali pada zaman malloc / gratis.)
Lain kali Anda membutuhkan sumber daya yang Anda pikir Anda kelola menjadi lebih kuat. Misalnya, mengapa Anda harus menutup koneksi itu? Pada akhirnya harus didasarkan pada beberapa jenis I / O yang disediakan oleh sistem (socket, file, apa pun), jadi mengapa Anda tidak dapat mengandalkan sistem untuk menutupnya untuk Anda ketika sumber daya level terendah diberikan? Jika server di ujung yang lain benar-benar meminta Anda untuk menutup koneksi dengan bersih daripada menjatuhkan soket, lalu apa yang akan terjadi ketika seseorang tersandung kabel listrik dari mesin yang kode Anda jalani, atau jaringan intervening padam?
Penafian: Saya pernah mengerjakan implementasi JVM di masa lalu. Saya benci finalizers.
sumber
finalize()
tanpa memberikan alasan seseorang benar-benar ingin menggunakannya.java.lang.ref.Reference
dan subkelasnya. Java 1 memang memiliki variabel :-) Dan ya, itu juga finalizer.Aturan sederhana: jangan pernah gunakan finalizer.
Fakta saja bahwa suatu objek memiliki finalizer (terlepas dari kode apa yang dijalankannya) sudah cukup untuk menyebabkan overhead yang cukup besar untuk pengumpulan sampah.
Dari sebuah artikel oleh Brian Goetz:
sumber
Satu-satunya waktu saya menggunakan menyelesaikan dalam kode produksi adalah untuk mengimplementasikan pemeriksaan bahwa sumber daya objek tertentu telah dibersihkan, dan jika tidak, maka log pesan yang sangat vokal. Itu tidak benar-benar mencoba dan melakukannya sendiri, itu hanya berteriak jika tidak dilakukan dengan benar. Ternyata cukup berguna.
sumber
Saya sudah melakukan Java secara profesional sejak tahun 1998, dan saya tidak pernah menerapkannya
finalize()
. Tidak sekali.sumber
Jawaban yang diterima baik, saya hanya ingin menambahkan bahwa sekarang ada cara untuk memiliki fungsi menyelesaikan tanpa benar-benar menggunakannya sama sekali.
Lihatlah kelas "Referensi". Referensi lemah, Referensi Phantom & Referensi Lunak.
Anda dapat menggunakannya untuk menyimpan referensi ke semua objek Anda, tetapi referensi ini SAJA tidak akan menghentikan GC. Hal yang rapi tentang ini adalah Anda dapat memanggilnya metode ketika akan dihapus, dan metode ini dapat dijamin untuk dipanggil.
Adapun finalisasi: Saya menggunakan finalisasi sekali untuk memahami objek apa yang dibebaskan. Anda dapat memainkan beberapa game yang apik dengan statika, penghitungan referensi, dan semacamnya - tetapi itu hanya untuk analisis, tetapi perhatikan kode seperti ini (tidak hanya dalam penyelesaian, tetapi di situlah Anda kemungkinan besar akan melihatnya):
Ini pertanda bahwa seseorang tidak tahu apa yang mereka lakukan. "Membersihkan" seperti ini hampir tidak pernah dibutuhkan. Ketika kelas GC, ini dilakukan secara otomatis.
Jika Anda menemukan kode seperti itu dalam penyelesaian, dijamin orang yang menulisnya bingung.
Jika ada di tempat lain, bisa jadi kode tersebut merupakan tambalan yang valid untuk model yang buruk (kelas tetap ada untuk waktu yang lama dan untuk beberapa alasan hal-hal yang dirujuk harus dibebaskan secara manual sebelum objek GC'd). Pada umumnya itu karena seseorang lupa untuk menghapus pendengar atau sesuatu dan tidak dapat mengetahui mengapa objek mereka tidak menjadi GC sehingga mereka hanya menghapus hal-hal yang dimaksud dan mengangkat bahu mereka dan berjalan pergi.
Seharusnya tidak pernah digunakan untuk membersihkan segalanya "Lebih cepat".
sumber
Saya tidak yakin apa yang bisa Anda lakukan dengan ini, tapi ...
Jadi saya kira Matahari menemukan beberapa kasus di mana (mereka pikir) itu harus digunakan.
sumber
finalize
daripada menggunakannya?$FAVORITE_IDE
.====================================
hasil:
=====================================
Jadi, Anda dapat membuat instance yang tidak terjangkau dapat dicapai dalam metode finalisasi.
sumber
System.gc();
bukan jaminan bahwa pengumpul sampah akan benar-benar berjalan. Ini adalah kebijakan penuh GC untuk membersihkan tumpukan (mungkin masih ada tumpukan yang cukup bebas di sekitar, mungkin tidak terfragmentasi menjadi banyak, ...). Jadi itu hanya permintaan sederhana dari programmer "tolong, bisakah Anda mendengarkan saya dan lari?" dan bukan perintah "jalankan sekarang seperti yang saya katakan!". Maaf telah menggunakan kata-kata bahasa tetapi kata itu persis bagaimana GC JVM bekerja.finalize()
dapat bermanfaat untuk menangkap kebocoran sumber daya. Jika sumber daya harus ditutup tetapi tidak menulis fakta bahwa itu tidak ditutup ke file log dan tutup. Dengan begitu Anda menghapus kebocoran sumber daya dan memberi diri Anda cara untuk mengetahui bahwa itu telah terjadi sehingga Anda dapat memperbaikinya.Saya telah pemrograman di Jawa sejak 1.0 alpha 3 (1995) dan saya belum menimpa finalisasi untuk apa pun ...
sumber
Anda tidak harus bergantung pada menyelesaikan () untuk membersihkan sumber daya Anda untuk Anda. menyelesaikan () tidak akan berjalan sampai kelas dikumpulkan sampah, jika demikian. Jauh lebih baik untuk membebaskan sumber daya secara eksplisit ketika Anda selesai menggunakannya.
sumber
Untuk menyorot poin dalam jawaban di atas: finalizers akan dieksekusi di thread GC sendirian. Saya telah mendengar tentang demo Sun utama di mana pengembang menambahkan sedikit tidur untuk beberapa finalizers dan sengaja membawa demo 3D yang mewah.
Yang terbaik untuk dihindari, dengan kemungkinan pengecualian dari tes-env diagnostik.
Eckel's Thinking in Java memiliki bagian yang bagus tentang ini.
sumber
Hmmm, saya pernah menggunakannya untuk membersihkan benda-benda yang tidak dikembalikan ke kolam yang ada.
Mereka sering dilewati, jadi tidak mungkin untuk mengatakan kapan mereka bisa kembali ke kolam dengan aman. Masalahnya adalah bahwa itu memberikan penalti besar selama pengumpulan sampah yang jauh lebih besar daripada penghematan dari mengumpulkan benda-benda. Itu dalam produksi selama sekitar satu bulan sebelum saya mencabut seluruh kumpulan, membuat semuanya dinamis dan selesai dengan itu.
sumber
Berhati - hatilah dengan apa yang Anda lakukan dalam
finalize()
. Terutama jika Anda menggunakannya untuk hal-hal seperti menelepon tutup () untuk memastikan sumber daya dibersihkan. Kami mengalami beberapa situasi di mana kami memiliki pustaka JNI yang ditautkan ke kode java yang sedang berjalan, dan dalam keadaan apa pun di mana kami menggunakan finalize () untuk memanggil metode JNI, kami akan mendapatkan korupsi tumpukan java yang sangat buruk. Korupsi tidak disebabkan oleh kode JNI yang mendasarinya sendiri, semua jejak memori baik-baik saja di perpustakaan asli. Itu hanya fakta bahwa kami memanggil metode JNI dari finalize () sama sekali.Ini dengan JDK 1.5 yang masih digunakan secara luas.
Kami tidak akan menemukan bahwa ada yang tidak beres sampai beberapa waktu kemudian, tetapi pada akhirnya pelakunya selalu merupakan metode finalisasi () yang memanfaatkan panggilan JNI.
sumber
Saat menulis kode yang akan digunakan oleh pengembang lain yang membutuhkan semacam metode "pembersihan" dipanggil untuk membebaskan sumber daya. Terkadang pengembang lain lupa memanggil metode pembersihan (atau tutup, atau hancurkan, atau apa pun) Anda. Untuk menghindari kemungkinan kebocoran sumber daya, Anda dapat memeriksa dalam metode finalisasi untuk memastikan bahwa metode itu dipanggil dan jika tidak, Anda dapat menyebutnya sendiri.
Banyak pengandar basis data melakukan ini dalam implementasi Pernyataan dan Koneksi mereka untuk memberikan sedikit keamanan terhadap pengembang yang lupa memanggil mereka.
sumber
Sunting: Oke, itu benar-benar tidak berhasil. Saya menerapkannya dan berpikir jika gagal kadang-kadang itu tidak masalah bagi saya tetapi bahkan tidak memanggil metode finalisasi sekali saja.
Saya bukan seorang programmer profesional tetapi dalam program saya, saya memiliki case yang saya pikir menjadi contoh dari kasus yang baik menggunakan finalize (), yaitu cache yang menulis kontennya ke disk sebelum dihancurkan. Karena tidak perlu dijalankan setiap kali pada kehancuran, itu hanya mempercepat program saya, saya harap itu tidak salah.
sumber
Mungkin berguna untuk menghapus hal-hal yang telah ditambahkan ke tempat global / statis (tidak perlu), dan perlu dihapus ketika objek dihapus. Contohnya:
sumber
this
instance yang diteruskan ke konstruktor, instance itu tidak akan pernah menjadi tidak terjangkau, oleh karena itu bagaimanapunfinalize()
juga tidak akan pernah bersih. Jika, di sisi lain, pendengar memegang referensi lemah ke objek itu, seperti namanya, konstruksi ini beresiko dibersihkan pada saat-saat yang seharusnya tidak. Siklus hidup objek bukan alat yang tepat untuk menerapkan semantik aplikasi.iirc - Anda dapat menggunakan metode finalisasi sebagai cara menerapkan mekanisme penyatuan untuk sumber daya yang mahal - sehingga mereka tidak mendapatkan GC juga.
sumber
Sebagai catatan:
Sumber: finalize () usang di java-9
sumber
Sumber daya (File, Socket, Stream dll.) Perlu ditutup setelah kita selesai dengan mereka. Mereka umumnya memiliki
close()
metode yang biasanya kita sebut dalamfinally
bagiantry-catch
pernyataan. Kadangfinalize()
- kadang juga dapat digunakan oleh beberapa pengembang tetapi IMO yang bukan cara yang cocok karena tidak ada jaminan bahwa penyelesaian akan dipanggil selalu.Di Java 7 kami telah mendapatkan pernyataan coba-dengan-sumber daya yang dapat digunakan seperti:
Dalam contoh di atas coba-dengan-sumber daya akan secara otomatis menutup sumber daya
BufferedReader
dengan menggunakanclose()
metode. Jika kita mau, kita juga bisa mengimplementasikan Closeable di kelas kita sendiri dan menggunakannya dengan cara yang sama. IMO sepertinya lebih rapi dan mudah dipahami.sumber
Secara pribadi, saya hampir tidak pernah menggunakan
finalize()
kecuali dalam satu keadaan langka: Saya membuat koleksi tipe generik khusus, dan saya menulisfinalize()
metode kustom yang melakukan hal berikut:(
CompleteObject
Adalah sebuah antarmuka saya membuat yang memungkinkan Anda menentukan bahwa Anda telah menerapkan jarang-dilaksanakanObject
metode seperti#finalize()
,#hashCode()
, dan#clone()
)Jadi, menggunakan
#setDestructivelyFinalizes(boolean)
metode saudari , program menggunakan koleksi saya dapat (membantu) menjamin bahwa menghancurkan referensi ke koleksi ini juga menghancurkan referensi ke isinya dan membuang semua jendela yang mungkin membuat JVM tetap hidup tanpa disengaja. Saya dianggap juga menghentikan semua utas, tetapi itu membuka kaleng cacing yang sama sekali baru.sumber
finalize()
pada AndaCompleteObject
contoh seperti yang Anda lakukan dalam kode di atas menyiratkan bahwa itu mungkin akan dipanggil dua kali, sebagai JVM masih mungkin memanggilfinalize()
sekali benda-benda ini benar-benar menjadi tidak terjangkau, yang, karena logika finalisasi, mungkin sebelum itufinalize()
metode koleksi khusus Anda dipanggil atau bahkan secara bersamaan pada saat yang sama. Karena saya tidak melihat langkah-langkah untuk memastikan keamanan benang dalam metode Anda, Anda tampaknya tidak mengetahui hal ini ...Jawaban yang diterima mencantumkan bahwa penutupan sumber daya selama penyelesaian dapat dilakukan.
Namun jawaban ini menunjukkan bahwa setidaknya di java8 dengan kompiler JIT, Anda mengalami masalah yang tidak terduga di mana kadang-kadang finalizer dipanggil bahkan sebelum Anda selesai membaca dari aliran yang dikelola oleh objek Anda.
Jadi, bahkan dalam situasi itu penyelesaian panggilan tidak akan direkomendasikan.
sumber