Mengapa metode finalisasi termasuk dalam Java?

44

Menurut posting ini , kita seharusnya tidak pernah mengandalkan metode finalisasi untuk dipanggil. Jadi mengapa Java memasukkannya ke dalam bahasa pemrograman?

Sepertinya keputusan yang mengerikan untuk memasukkan dalam bahasa pemrograman suatu fungsi yang mungkin dipanggil.

patstuart
sumber

Jawaban:

41

Menurut Joshua Bloch's Java Efektif (Edisi Kedua), ada dua skenario kapan finalize()berguna:

  1. Pertama adalah bertindak sebagai "jaring pengaman" jika pemilik benda lupa memanggil metode penghentian eksplisitnya. Meskipun tidak ada jaminan bahwa finalizer akan dipanggil segera, mungkin lebih baik untuk membebaskan sumber daya terlambat daripada tidak pernah, dalam kasus-kasus (mudah-mudahan jarang) ketika klien gagal memanggil metode penghentian eksplisit. Tetapi finalizer harus mencatat peringatan jika menemukan bahwa sumber daya belum diakhiri

  2. Penggunaan sah kedua finalizers menyangkut objek dengan rekan asli. Peer asli adalah objek asli yang didelegasikan objek normal melalui metode asli. Karena peer asli bukan objek normal, pemulung tidak tahu tentang hal itu dan tidak dapat mengklaim kembali ketika peer Java-nya direklamasi. Finalizer adalah kendaraan yang tepat untuk melakukan tugas ini, dengan asumsi rekan asli tidak memiliki sumber daya penting. Jika rekan asli memegang sumber daya yang harus dihentikan segera, kelas harus memiliki metode penghentian eksplisit, seperti yang dijelaskan di atas. Metode penghentian harus melakukan apa pun yang diperlukan untuk membebaskan sumber daya kritis.

Untuk bacaan lebih lanjut, lihat Butir 7, halaman 27.

bbalchev
sumber
4
Dengan # 2 terdengar seperti peer asli adalah sumber daya asli yang membutuhkan jaring pengaman dan harus memiliki metode terminasi, dan dengan demikian tidak boleh menjadi titik yang terpisah.
Mooing Duck
2
@MooingDuck: # 2 adalah poin terpisah karena, jika peer asli tidak memiliki sumber daya kritis (mis. Itu murni objek di memori), maka tidak perlu mengekspos metode terminasi eksplisit. Jaring pengaman dari # 1 secara eksplisit tentang memiliki metode terminasi.
jhominal
55

Finalizers penting untuk pengelolaan sumber daya asli. Misalnya, objek Anda mungkin perlu mengalokasikan WidgetHandle dari sistem operasi menggunakan API non-Java. Jika Anda tidak melepaskan WidgetHandle ketika objek Anda adalah GC, Anda akan membocorkan WidgetHandles.

Yang penting adalah bahwa "finalizer tidak pernah disebut" kasus rusak agak sederhana:

  1. Program dimatikan dengan cepat
  2. Objek "hidup selamanya" selama masa program
  3. Komputer mati / proses Anda terbunuh oleh OS / etc

Dalam ketiga kasus ini, Anda juga tidak memiliki kebocoran asli (berdasarkan fakta bahwa program Anda tidak berjalan lagi), atau Anda sudah memiliki kebocoran non-asli (jika Anda terus mengalokasikan objek yang dikelola tanpa menjadi GC).

Peringatan "jangan mengandalkan finalizer yang dipanggil" sebenarnya tentang tidak menggunakan finalizer untuk logika program. Misalnya, Anda tidak ingin melacak berapa banyak objek Anda yang ada di semua contoh program Anda dengan menambah penghitung dalam file di suatu tempat selama konstruksi dan mengurangi itu dalam finalizer - karena tidak ada jaminan bahwa objek Anda akan diselesaikan, penghitung file ini mungkin tidak akan pernah kembali ke 0. Ini benar-benar kasus khusus dari prinsip yang lebih umum bahwa Anda tidak harus bergantung pada program Anda mengakhiri secara normal (gangguan listrik, dll).

Namun, untuk pengelolaan sumber daya asli, kasus di mana finalizer tidak berjalan sesuai dengan kasus di mana Anda tidak peduli jika tidak berjalan.

Ryan Cavanaugh
sumber
Jawaban yang sangat bagus. Saya seperti bahkan jika itu bisa disebut .. itu harus tetap dimasukkan. Saya bingung untuk sementara waktu dengan pertanyaan dan pertanyaan terkait di StackOverflow.
InformedA
3
Itu tidak ada dalam pertanyaan, tetapi perlu dibahas dalam konteks "jangan bergantung pada finalizer yang dipanggil": Finalizer dapat dipanggil, tetapi hanya setelah penundaan yang lama secara sewenang-wenang setelah objek menjadi tidak terjangkau. Ini penting untuk "sumber daya asli" yang jauh lebih langka daripada ingatan (yang kebanyakan dari mereka, ternyata).
26
"Finalizers penting untuk pengelolaan sumber daya asli." - tidaaaaaak, maaf Menggunakan finalizer untuk mengelola sumber daya asli adalah ide yang mengerikan (itulah sebabnya kami memiliki autocloseable dan co sekarang). GC hanya peduli dengan memori, bukan sumber daya lainnya. Ini berarti Anda dapat kehabisan sumber daya asli tetapi masih memiliki cukup memori untuk tidak memicu GC - kami benar-benar tidak menginginkannya. Juga finalizers membuat GC lebih mahal yang bukan ide yang baik juga
Voo
12
Saya setuju Anda masih harus memiliki strategi manajemen sumber daya asli eksplisit selain finalizers, tetapi jika objek Anda mengalokasikannya dan tidak membebaskan mereka di finalizer, Anda membuka diri terhadap kebocoran asli dalam kasus di mana objek tersebut berada GC tanpa pembebasan sumber daya secara eksplisit. Dengan kata lain, finalizer adalah bagian penting dari strategi manajemen sumber daya asli, bukan solusi untuk masalah secara umum.
Ryan Cavanaugh
1
@ Voo mungkin lebih tepat untuk mengatakan finalizer adalah fallback jika kontrak objek tidak diikuti ( close()tidak pernah dipanggil sebelum menjadi tidak terjangkau)
ratchet freak
5

Tujuan metode ini dijelaskan dalam dokumentasi API sebagai berikut:

itu dipanggil jika dan ketika mesin virtual Java telah menentukan bahwa tidak ada lagi sarana dengan mana objek ini dapat diakses oleh utas apa pun yang belum mati, kecuali sebagai akibat dari tindakan yang diambil oleh finalisasi beberapa objek lain atau kelas yang siap diselesaikan ...

tujuan umum finalize... adalah untuk melakukan tindakan pembersihan sebelum objek dibuang secara tidak dapat ditarik kembali . Misalnya, metode finalisasi untuk objek yang mewakili koneksi input / output mungkin melakukan transaksi I / O eksplisit untuk memutus koneksi sebelum objek tersebut dibuang secara permanen ...


Jika Anda juga tertarik pada alasan mengapa perancang bahasa telah memilih bahwa "objek dibuang secara tidak dapat ditarik kembali" ( sampah dikumpulkan ) dengan cara yang berada di luar kendali pemrogram aplikasi ("kita seharusnya tidak pernah mengandalkan"), ini telah dijelaskan dalam jawaban terkait pertanyaan :

pengumpulan sampah otomatis ... menghilangkan seluruh kelas kesalahan pemrograman yang mengganggu programmer C dan C ++. Anda dapat mengembangkan kode Java dengan keyakinan bahwa sistem akan menemukan banyak kesalahan dengan cepat dan bahwa masalah utama tidak akan aktif sampai kode produksi Anda dikirimkan.

Kutipan di atas, pada gilirannya, diambil dari dokumentasi resmi tentang tujuan desain Java , yang dapat dianggap referensi otoritatif menjelaskan mengapa desainer bahasa Jawa memutuskan dengan cara ini.

Untuk diskusi agnostik bahasa yang lebih rinci dan preferensi ini, lihat bagian OOSC 9.6 Manajemen memori otomatis (sebenarnya, tidak hanya bagian ini tetapi seluruh bab 9 sangat layak dibaca jika Anda tertarik pada hal-hal seperti itu). Bagian ini dibuka dengan pernyataan yang tidak ambigu:

Lingkungan OO yang baik harus menawarkan mekanisme manajemen memori otomatis yang akan mendeteksi dan mendapatkan kembali objek yang tidak terjangkau, memungkinkan pengembang aplikasi untuk berkonsentrasi pada pengembangan aplikasi pekerjaan mereka.

Diskusi sebelumnya harus cukup untuk menunjukkan betapa pentingnya memiliki fasilitas seperti itu tersedia. Dalam kata-kata Michael Schweitzer dan Lambert Strether:

Program berorientasi objek tanpa manajemen memori otomatis kira-kira sama dengan pressure cooker tanpa katup pengaman: cepat atau lambat benda itu pasti akan meledak!

agas
sumber
4

Penyelesai ada karena mereka diharapkan menjadi sarana yang efektif untuk memastikan bahwa segala sesuatunya dibersihkan (meskipun dalam praktiknya tidak), dan karena ketika ditemukan, cara yang lebih baik untuk memastikan pembersihan (seperti referensi hantu dan percobaan dengan -resources) belum ada. Jika dipikir-pikir, Jawa mungkin akan lebih baik jika upaya yang dihabiskan untuk mengimplementasikan fasilitas "finalisasinya" telah dihabiskan untuk sarana pembersihan lainnya, tetapi itu hampir tidak jelas selama waktu Jawa awalnya dikembangkan.

supercat
sumber
3

CAVEAT: Saya mungkin ketinggalan zaman, tetapi ini adalah pemahaman saya beberapa tahun yang lalu:

Secara umum, tidak ada jaminan kapan finalis berjalan - atau bahkan itu berjalan sama sekali, meskipun beberapa JVM akan memungkinkan Anda meminta GC lengkap dan finalisasi sebelum program keluar (yang, tentu saja, berarti program membutuhkan waktu lebih lama untuk keluar, dan yang bukan mode operasi standar).

Dan beberapa GC diketahui secara eksplisit menunda atau menghindari objek GC'ing yang telah menyelesaikan, dengan harapan bahwa ini akan menghasilkan kinerja yang lebih baik pada tolok ukur.

Sayangnya, perilaku ini bertentangan dengan alasan asli yang disarankan oleh finalizer, dan telah mendorong penggunaan metode shutdown yang disebut secara eksplisit.

Jika Anda memiliki objek yang benar-benar harus dibersihkan sebelum dibuang, dan jika Anda benar-benar tidak dapat mempercayai pengguna untuk melakukannya, finalizer mungkin masih layak dipertimbangkan. Tetapi secara umum, ada Alasan Bagus bahwa Anda tidak melihatnya sesering dalam kode Java modern seperti yang Anda lakukan dalam beberapa contoh awal.

keshlam
sumber
1

The Google Java Style Guide memiliki beberapa nasihat bijak pada subjek:

Sangat jarang ditimpa Object.finalize.

Kiat : Jangan lakukan itu. Jika Anda benar-benar harus, pertama-tama baca dan pahami Java Efektif Item 7, "Hindari Penyelesaikan," dengan sangat hati-hati, dan kemudian jangan lakukan itu.

Daniel Pryden
sumber
5
Mengapa saya mengagumi penyederhanaan masalah yang berlebihan, "jangan lakukan itu" bukanlah jawaban untuk "mengapa itu ada".
Pierre Arlaud
1
Saya kira saya tidak menguraikannya dengan baik dalam jawaban saya, tetapi (IMO) jawaban untuk "mengapa itu ada?" adalah "tidak seharusnya". Untuk kasus-kasus langka di mana Anda benar-benar membutuhkan operasi finalisasi, Anda mungkin ingin membangunnya sendiri PhantomReferencedan ReferenceQueuesebagai gantinya.
Daniel Pryden
Maksud saya sementara * jelas, meskipun saya tidak menurunkan suara Anda. Namun, jawaban yang paling banyak dibuktikan menunjukkan betapa bergunanya metode ini, agak membuat Anda tidak valid.
Pierre Arlaud
1
Bahkan dalam kasus peer asli, saya pikir PhantomReferenceini solusi yang lebih baik. Finalizers adalah kutil yang tersisa dari masa-masa awal Jawa, dan sejenis Object.clone()dan mentah, adalah bagian dari bahasa yang paling terlupakan.
Daniel Pryden
1
Bahaya finalizer dijelaskan dengan cara yang lebih mudah diakses (dimengerti oleh pengembang) dalam C #. , Meskipun demikian, seseorang tidak boleh menyiratkan bahwa mekanisme yang mendasari di Jawa dan C # adalah sama; tidak ada dalam spesifikasi mereka yang mengatakan demikian.
rwong
1

The Java Language Specification (Java SE 7) menyatakan:

Finalizers memberikan peluang untuk membebaskan sumber daya yang tidak dapat dibebaskan secara otomatis oleh manajer penyimpanan otomatis. Dalam situasi seperti itu, hanya mendapatkan kembali memori yang digunakan oleh suatu objek tidak akan menjamin bahwa sumber daya yang dipegangnya akan direklamasi.

Benni
sumber