Apakah menyetel objek Java ke null melakukan sesuatu lagi?

112

Saya menjelajahi beberapa buku lama dan menemukan salinan "Praktis Java" oleh Peter Hagger. Di bagian performance, terdapat rekomendasi untuk mengatur referensi objek nullsaat tidak diperlukan lagi.

Di Java, apakah pengaturan referensi objek untuk nullmeningkatkan kinerja atau efisiensi pengumpulan sampah? Jika ya, dalam kasus apa ini menjadi masalah? Kelas kontainer? Komposisi objek? Kelas batin anonim?

Saya sering melihat ini dalam kode. Apakah saran pemrograman ini sekarang sudah usang atau masih berguna?

sal
sumber
2
Profil itu. Pada runtime modern, Anda tidak akan melihat peningkatan yang berarti dalam performa atau footprint memori.
Jason Coco
12
@Jason, Profil itu? Itu mengasumsikan saya akan membuat profil sekumpulan kasus yang cukup besar untuk mendapatkan hasil yang cukup baik untuk menjawab ini. Dan saya tidak memilih sekumpulan kasus yang VM cukup dioptimalkan untuk menutupi masalah gc dan kinerja. Itu sebabnya saya menanyakan ini di sini. Untuk memahami kasus-kasus yang menjadi masalah ini.
sal
1
Duplikat stackoverflow.com/questions/449409/… .
Michael Myers

Jawaban:

73

Itu sedikit tergantung pada saat Anda berpikir untuk membatalkan referensi.

Jika Anda memiliki rantai objek A-> B-> C, maka setelah A tidak dapat dijangkau, A, B, dan C semuanya akan memenuhi syarat untuk pengumpulan sampah (dengan asumsi tidak ada hal lain yang merujuk ke B atau C). Tidak perlu, dan tidak pernah ada kebutuhan, untuk secara eksplisit mengatur referensi A-> B atau B-> C ke null, misalnya.

Selain itu, sering kali masalah tidak benar-benar muncul, karena pada kenyataannya Anda berurusan dengan objek dalam koleksi. Biasanya Anda harus selalu berpikir untuk menghapus objek dari daftar, peta, dll dengan memanggil metode appropiate remove ().

Kasus di mana dulu ada beberapa saran untuk mengatur referensi ke nol secara khusus dalam lingkup panjang di mana objek intensif memori tidak lagi digunakan di tengah ruang lingkup . Sebagai contoh:

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;             <-- explicitly set to null
  doSomethingElse();
}

Alasannya di sini adalah karena obj masih dalam cakupan, maka tanpa penghapusan eksplisit referensi, ia tidak menjadi sampah yang dapat dikumpulkan sampai setelah metode doSomethingElse () selesai. Dan ini adalah saran yang mungkin tidak lagi berlaku pada JVM modern : ternyata kompiler JIT dapat bekerja pada titik mana referensi objek lokal tertentu tidak lagi digunakan.

Neil Coffey
sumber
2
Variabel lokal dapat dioptimalkan oleh mesin. Variabel kelas tidak bisa. Saya telah melihat utas pekerja (di kumpulan utas) tidak melepaskan objek status mereka setelah menangani permintaan dan dengan demikian menyematkan objek tersebut di memori sampai utas pekerja diberi tugas baru (yang segera menimpa objek status lama dengan yang baru). Dengan objek status besar dan ratusan utas pekerja (pikirkan: server http besar), ini bisa menjadi sejumlah besar memori yang disimpan dan disalin, namun tidak akan pernah digunakan lagi.
Brian White
1
Saya mungkin harus menambahkan: saran yang saya berikan di atas adalah saran yang harus diikuti secara umum, dengan asumsi perpustakaan berperilaku baik, VM berperilaku baik, dll. Jika Anda menemukan bahwa Anda memiliki, katakanlah, perpustakaan kumpulan benang yang bergantung pada objek setelahnya. sebuah pekerjaan telah selesai, yah ... itu adalah bug di pustaka kumpulan utas tersebut yang mungkin dapat diatasi dengan membatalkan referensi objek, tetapi sama-sama dapat diselesaikan dengan menggunakan pustaka kumpulan utas yang tidak terlalu bermasalah. Saya tidak yakin bahwa bug seperti itu mengubah prinsip desain umum.
Neil Coffey
25

Tidak, itu bukan nasihat usang. Referensi yang menggantung masih menjadi masalah, terutama jika Anda, katakanlah, mengimplementasikan wadah array yang dapat diperluas ( ArrayListatau sejenisnya) menggunakan array yang dialokasikan sebelumnya. Elemen di luar ukuran "logis" dari daftar harus dikosongkan, atau mereka tidak akan dibebaskan.

Lihat Effective Java 2nd ed, Item 6: Eliminate Obsolete Object References.

Chris Jester-Young
sumber
Apakah ini masalah bahasa atau masalah implementasi VM?
Pablo Santa Cruz
4
Ini adalah masalah "semantik". Pada dasarnya, karena Anda telah mengalokasikan array sebelumnya, VM melihatnya. Ia tidak tahu apa-apa tentang ukuran "logis" dari penampung Anda. Katakanlah Anda memiliki ArrayList berukuran 10, yang didukung oleh array 16 elemen. VM tidak dapat mengetahui bahwa item 10..15 sebenarnya tidak digunakan; jika slot tersebut memiliki konten, mereka tidak akan dibebaskan.
Chris Jester-Young
bagaimana dengan di luar kelas kontainer? Dalam komposisi objek di mana objek dalam tidak dialokasikan oleh objek luar.
sal
7
@sal Aturan umumnya adalah jika Anda tidak bisa mereferensikannya, sampah dikumpulkan. Jadi jika objek luar berisi referensi ke objek lain, dengan asumsi objek dalam tidak memiliki referensi lain, menyetel satu-satunya referensi objek luar ke null akan menyebabkan seluruh objek menjadi sampah yang dikumpulkan, termasuk referensi yatim piatu.
ubermensch
2
Dalam Butir 6 yang sama Anda sebutkan: Menghapus referensi objek harus menjadi pengecualian daripada norma.
ACV
10

Bidang contoh, elemen larik

Jika ada referensi ke suatu objek, maka tidak bisa sampah dikumpulkan. Apalagi jika objek itu (dan seluruh grafik di belakangnya) besar, hanya ada satu referensi yang menghentikan pengumpulan sampah, dan referensi itu tidak terlalu dibutuhkan lagi, itu adalah situasi yang sangat disayangkan.

Kasus patologis adalah objek yang mempertahankan instance unnessary ke seluruh pohon DOM XML yang digunakan untuk mengkonfigurasinya, MBean yang tidak dicabut, atau referensi tunggal ke objek dari aplikasi web yang tidak diterapkan yang mencegah seluruh classloader dibongkar .

Jadi, kecuali Anda yakin bahwa objek yang menyimpan referensi itu sendiri akan tetap menjadi sampah yang dikumpulkan (atau bahkan kemudian), Anda harus membatalkan semua yang tidak lagi Anda perlukan.

Variabel yang dicakup:

Jika Anda mempertimbangkan untuk menyetel variabel lokal ke null sebelum akhir cakupannya, sehingga dapat diklaim kembali oleh pengumpul sampah dan menandainya sebagai "tidak dapat digunakan mulai sekarang", Anda harus mempertimbangkan untuk meletakkannya dalam ruang lingkup yang lebih terbatas sebagai gantinya .

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;          //   <-- explicitly set to null
  doSomethingElse();
}

menjadi

{
  {  
     BigObject obj = ...
     doSomethingWith(obj);
  }    //         <-- obj goes out of scope
  doSomethingElse();
}

Cakupan yang panjang dan datar biasanya buruk untuk keterbacaan kode juga. Memperkenalkan metode pribadi untuk memecah masalah hanya untuk tujuan itu bukanlah hal yang tidak biasa juga.

Thilo
sumber
Jika Anda mengacu pada referensi yang kuat, ya ini benar, tetapi tidak untuk semua referensi. Referensi yang lemah di java dapat dikumpulkan dari sampah.
James Drinkard
5

Dalam lingkungan yang membatasi memori (misalnya ponsel) ini dapat berguna. Dengan menyetel null, objetc tidak perlu menunggu variabel keluar dari ruang lingkup untuk di-gc.

Untuk pemrograman sehari-hari, bagaimanapun, ini seharusnya tidak menjadi aturan, kecuali dalam kasus-kasus khusus seperti yang dikutip oleh Chris Jester-Young.

besen
sumber
1

Pertama, Ini tidak berarti apa pun yang Anda tetapkan sebagai objek null. Saya jelaskan di bawah ini:

List list1 = new ArrayList();
List list2 = list1;

Pada segmen kode di atas kita membuat nama variabel referensi objek list1dari ArrayListobjek yang disimpan di memori. Jadi list1mengacu pada objek itu dan itu tidak lebih dari sebuah variabel. Dan di baris kedua kode kita menyalin referensi list1ke list2. Jadi sekarang kembali ke pertanyaan Anda jika saya melakukannya:

list1 = null;

itu berarti list1tidak lagi mengacu pada objek apa pun yang disimpan dalam memori sehingga list2juga tidak memiliki referensi apa pun . Jadi jika Anda memeriksa ukuran list2:

list2.size(); //it gives you 0

Jadi di sini konsep pengumpul sampah muncul yang mengatakan «Anda tidak perlu khawatir tentang membebaskan memori yang dipegang oleh objek, saya akan melakukannya ketika saya menemukan bahwa itu tidak lagi digunakan dalam program dan JVM akan mengatur saya.»

Saya harap konsepnya jelas.

Aman Goel
sumber
0

Salah satu alasan untuk melakukannya adalah untuk menghilangkan referensi objek usang. Anda dapat membaca teksnya di sini.

Sudip Bhandari
sumber