Apakah Anda perlu membuang objek dan mengaturnya ke nol, atau akankah pengumpul sampah membersihkannya ketika mereka keluar dari ruang lingkup?
310
Apakah Anda perlu membuang objek dan mengaturnya ke nol, atau akankah pengumpul sampah membersihkannya ketika mereka keluar dari ruang lingkup?
Dispose()
metode Anda ! Ini adalah variasi halus pada pertanyaan ini, tetapi penting karena objek yang dibuang tidak dapat mengetahui apakah itu "keluar dari ruang lingkup" (panggilanDispose()
bukan jaminan). Lebih lanjut di sini: stackoverflow.com/questions/6757048/…Jawaban:
Objek akan dibersihkan ketika mereka tidak lagi digunakan dan ketika pengumpul sampah merasa cocok. Terkadang, Anda mungkin perlu mengatur objek
null
membuatnya keluar dari ruang lingkup (seperti bidang statis yang nilainya tidak Anda butuhkan lagi), tetapi secara keseluruhan biasanya tidak perlu diatur kenull
.Mengenai membuang benda, saya setuju dengan @Andre. Jika objek
IDisposable
itu adalah ide yang baik untuk membuangnya ketika Anda tidak lagi membutuhkannya, terutama jika objek tersebut menggunakan sumber daya yang tidak dikelola. Tidak membuang sumber daya yang tidak dikelola akan menyebabkan kebocoran memori .Anda dapat menggunakan
using
pernyataan untuk secara otomatis membuang objek setelah program Anda meninggalkan ruang lingkupusing
pernyataan.Yang secara fungsional setara dengan:
sumber
if (obj != null) ((IDisposable)obj).Dispose();
IDisposable
. Gagal membuang objek umumnya tidak akan menyebabkan kebocoran memori pada kelas yang dirancang dengan baik. Ketika bekerja dengan sumber daya yang tidak dikelola di C # Anda harus memiliki finalizer yang masih akan melepaskan sumber daya yang tidak dikelola. Ini berarti bahwa alih-alih mendelokasi sumber daya ketika harus dilakukan, itu akan ditunda ketika pengumpul sampah menyelesaikan objek yang dikelola. Ini masih dapat menyebabkan banyak masalah lain (seperti kunci yang belum dirilis). Anda harus BuangIDisposable
meskipun!Objek tidak pernah keluar dari ruang lingkup di C # seperti yang mereka lakukan di C ++. Mereka ditangani oleh Pengumpul Sampah secara otomatis ketika mereka tidak digunakan lagi. Ini adalah pendekatan yang lebih rumit daripada C ++ di mana ruang lingkup variabel sepenuhnya deterministik. Pengumpul sampah CLR secara aktif menelusuri semua objek yang telah dibuat dan berfungsi jika sedang digunakan.
Objek bisa "keluar dari ruang lingkup" dalam satu fungsi tetapi jika nilainya dikembalikan, maka GC akan melihat apakah fungsi panggilan memegang nilai kembali.
Mengatur referensi objek
null
tidak perlu karena pengumpulan sampah berfungsi dengan menentukan objek mana yang sedang direferensikan oleh objek lain.Dalam praktiknya, Anda tidak perlu khawatir tentang kehancuran, itu hanya bekerja dan itu bagus :)
Dispose
harus dipanggil pada semua objek yang mengimplementasikanIDisposable
ketika Anda selesai bekerja dengannya. Biasanya Anda akan menggunakanusing
blok dengan objek-objek seperti:EDIT Pada lingkup variabel. Craig telah bertanya apakah ruang lingkup variabel memiliki efek pada masa hidup objek. Untuk menjelaskan dengan benar aspek CLR itu, saya perlu menjelaskan beberapa konsep dari C ++ dan C #.
Lingkup variabel aktual
Dalam kedua bahasa variabel hanya dapat digunakan dalam lingkup yang sama seperti yang didefinisikan - kelas, fungsi atau blok pernyataan terlampir oleh kurung kurawal. Perbedaan yang halus, bagaimanapun, adalah bahwa dalam C #, variabel tidak dapat didefinisikan ulang dalam blok bersarang.
Di C ++, ini sah menurut hukum:
Dalam C #, namun Anda mendapatkan kesalahan kompiler:
Ini masuk akal jika Anda melihat MSIL yang dihasilkan - semua variabel yang digunakan oleh fungsi didefinisikan pada awal fungsi. Lihatlah fungsi ini:
Di bawah ini adalah IL yang dihasilkan. Perhatikan bahwa iVal2, yang didefinisikan di dalam blok if sebenarnya didefinisikan pada level fungsi. Secara efektif ini berarti bahwa C # hanya memiliki ruang lingkup tingkat kelas dan fungsi sejauh variabel yang bersangkutan.
C ++ lingkup dan objek seumur hidup
Setiap kali variabel C ++, dialokasikan pada stack, keluar dari ruang lingkup itu akan hancur. Ingatlah bahwa di C ++ Anda dapat membuat objek di stack atau di heap. Ketika Anda membuatnya di stack, setelah eksekusi meninggalkan ruang lingkup, mereka akan muncul dari stack dan dihancurkan.
Ketika objek C ++ dibuat pada heap, mereka harus dihancurkan secara eksplisit, kalau tidak itu adalah kebocoran memori. Tidak ada masalah dengan variabel stack.
C # Obyek Seumur Hidup
Dalam CLR, objek (yaitu tipe referensi) selalu dibuat pada heap yang dikelola. Ini diperkuat oleh sintaks penciptaan objek. Pertimbangkan potongan kode ini.
Dalam C ++ ini akan membuat instance aktif
MyClass
di stack dan memanggil konstruktor default-nya. Dalam C # itu akan membuat referensi ke kelasMyClass
yang tidak menunjuk ke apa pun. Satu-satunya cara untuk membuat instance kelas adalah dengan menggunakannew
operator:Di satu sisi, objek C # sangat mirip objek yang dibuat menggunakan
new
sintaks di C ++ - mereka dibuat di heap tetapi tidak seperti objek C ++, mereka dikelola oleh runtime, jadi Anda tidak perlu khawatir merusaknya.Karena objek selalu di tumpukan fakta bahwa referensi objek (yaitu pointer) keluar dari ruang lingkup menjadi diperdebatkan. Ada lebih banyak faktor yang terlibat dalam menentukan apakah suatu objek harus dikumpulkan daripada sekadar kehadiran referensi ke objek.
C # Referensi objek
Jon Skeet membandingkan referensi objek di Jawa dengan potongan-potongan string yang melekat pada balon, yang merupakan objek. Analogi yang sama berlaku untuk referensi objek C #. Mereka hanya menunjuk ke lokasi tumpukan yang berisi objek. Dengan demikian, pengaturannya ke nol tidak memiliki efek langsung pada umur objek, balon terus ada, sampai GC "muncul" itu.
Melanjutkan analogi balon, akan tampak logis bahwa sekali balon tidak memiliki ikatan, balon itu dapat dihancurkan. Sebenarnya ini adalah persis bagaimana referensi menghitung objek bekerja dalam bahasa yang tidak dikelola Kecuali pendekatan ini tidak bekerja untuk referensi melingkar dengan sangat baik. Bayangkan dua balon yang disatukan oleh seutas tali tetapi tidak satu pun balon memiliki tali untuk yang lainnya. Di bawah aturan penghitungan ref sederhana, mereka berdua terus ada, meskipun seluruh kelompok balon "yatim piatu".
Objek NET sangat mirip balon helium di bawah atap. Ketika atap terbuka (GC run) - balon yang tidak digunakan melayang, meskipun mungkin ada kelompok balon yang ditambatkan bersama.
.NET GC menggunakan kombinasi GC generasi dan tanda serta sapuan. Pendekatan generasi melibatkan runtime yang mendukung untuk memeriksa objek yang telah dialokasikan terakhir, karena mereka lebih cenderung tidak digunakan dan menandai dan menyapu melibatkan runtime melalui seluruh grafik objek dan bekerja jika ada kelompok objek yang tidak digunakan. Ini cukup menangani masalah ketergantungan sirkuler.
Juga, .NET GC berjalan di utas lain (disebut utas finalizer) karena memiliki cukup banyak yang harus dilakukan dan melakukannya pada utas utama akan mengganggu program Anda.
sumber
Seperti yang orang lain katakan, Anda pasti ingin menelepon
Dispose
jika kelas diimplementasikanIDisposable
. Saya mengambil posisi yang cukup kaku dalam hal ini. Beberapa klaim kekuatan yang menyerukanDispose
padaDataSet
, misalnya, adalah sia-sia karena mereka dibongkar dan melihat bahwa itu tidak melakukan sesuatu yang berarti. Tapi, saya pikir ada banyak kesalahan dalam argumen itu.Bacalah ini untuk debat yang menarik oleh orang-orang terhormat tentang masalah ini. Kemudian bacalah alasan saya di sini mengapa saya pikir Jeffery Richter ada di kubu yang salah.
Sekarang, apakah Anda harus menetapkan referensi atau tidak
null
. Jawabannya adalah tidak. Biarkan saya menggambarkan poin saya dengan kode berikut.Jadi kapan menurut Anda objek yang dirujuk
a
memenuhi syarat untuk koleksi? Jika Anda mengatakan setelah panggilan kea = null
maka Anda salah. Jika Anda mengatakan setelahMain
metode selesai maka Anda juga salah. Jawaban yang benar adalah bahwa itu memenuhi syarat untuk pengumpulan suatu saat selama panggilan keDoSomething
. Itu benar. Itu memenuhi syarat sebelum referensi diatur kenull
dan mungkin bahkan sebelum panggilan untukDoSomething
selesai. Itu karena kompiler JIT dapat mengenali ketika referensi objek tidak lagi dereferensi bahkan jika mereka masih di-root.sumber
a
bidang anggota pribadi dalam kelas? Jikaa
tidak disetel ke nol, GC tidak memiliki cara untuk mengetahui apakaha
akan digunakan lagi dalam beberapa metode, bukan? Dengan demikiana
tidak akan dikumpulkan sampai seluruh kelas yang mengandung dikumpulkan. Tidak?a
anggota kelas dan kelas yang berisia
masih di-root dan digunakan maka itu juga akan berkeliaran. Itu adalah salah satu skenario di mana pengaturannyanull
bisa bermanfaat.Dispose
ini penting - tidak mungkin untuk memanggilDispose
(atau metode lain yang tidak dapat digariskan) pada suatu objek tanpa referensi yang di-rooting untuknya; memanggilDispose
setelah seseorang selesai menggunakan objek akan memastikan bahwa referensi yang di-rooting akan terus ada sepanjang durasi tindakan terakhir yang dilakukan padanya. Meninggalkan semua referensi ke suatu objek tanpa memanggilDispose
mungkin ironisnya memiliki efek menyebabkan sumber daya objek kadang-kadang dirilis terlalu dini .Anda tidak perlu mengatur objek ke nol di C #. Compiler dan runtime akan membantu mencari tahu ketika mereka tidak lagi dalam ruang lingkup.
Ya, Anda harus membuang objek yang mengimplementasikan IDisposable.
sumber
want
membatalkannya segera setelah selesai sehingga bebas untuk direklamasi.Saya setuju dengan jawaban umum di sini bahwa ya Anda harus membuang dan tidak, Anda biasanya tidak boleh mengatur variabel ke nol ... tapi saya ingin menunjukkan bahwa membuang BUKAN terutama tentang manajemen memori. Ya, ini dapat membantu (dan terkadang memang demikian) dengan manajemen memori, tetapi tujuan utamanya adalah untuk memberi Anda pelepasan sumber daya langka secara deterministik.
Misalnya, jika Anda membuka port perangkat keras (misalnya serial), soket TCP / IP, file (dalam mode akses eksklusif), atau bahkan koneksi basis data, kini Anda telah mencegah kode lain dari menggunakan item-item tersebut hingga item tersebut dirilis. Buang umumnya melepaskan barang-barang ini (bersama dengan GDI dan lainnya "os" menangani dll. Yang ada 1000-an tersedia, tetapi secara keseluruhan masih terbatas). Jika Anda tidak memanggil dipose pada objek pemilik dan secara eksplisit melepaskan sumber daya ini, maka cobalah untuk membuka sumber daya yang sama lagi di masa depan (atau program lain melakukannya) bahwa upaya terbuka akan gagal karena objek Anda yang tidak terlindungi, tidak terkumpul masih memiliki item terbuka . Tentu saja, ketika GC mengumpulkan item (jika pola Buang telah diterapkan dengan benar) sumber daya akan dilepaskan ... tetapi Anda tidak tahu kapan itu akan terjadi, jadi Anda tidak perlu t tahu kapan aman untuk membuka kembali sumber daya itu. Ini adalah masalah utama. Tentu saja, melepaskan pegangan ini sering juga melepaskan memori, dan tidak pernah melepaskannya mungkin tidak pernah melepaskan memori itu ... karena itu semua pembicaraan tentang kebocoran memori, atau keterlambatan pembersihan memori.
Saya telah melihat contoh dunia nyata dari masalah yang menyebabkan ini. Sebagai contoh, saya telah melihat aplikasi web ASP.Net yang akhirnya gagal terhubung ke database (walaupun untuk waktu yang singkat, atau sampai proses server web dimulai ulang) karena 'koneksi pool sql server penuh' ... yaitu , begitu banyak koneksi telah dibuat dan tidak secara eksplisit dirilis dalam periode waktu yang singkat sehingga tidak ada koneksi baru yang dapat dibuat dan banyak koneksi di pool, meskipun tidak aktif, masih dirujuk oleh objek yang tidak tertutup dan tidak terkumpul sehingga bisa ' t digunakan kembali. Membuang koneksi database dengan benar jika perlu memastikan masalah ini tidak terjadi (setidaknya tidak kecuali Anda memiliki akses bersamaan yang sangat tinggi).
sumber
Jika objek diimplementasikan
IDisposable
, maka ya, Anda harus membuangnya. Objek bisa bergantung pada sumber daya asli (file menangani, objek OS) yang mungkin tidak segera dibebaskan sebaliknya. Hal ini dapat menyebabkan kelaparan sumber daya, masalah penguncian file, dan bug halus lainnya yang dapat dihindari.Lihat juga Menerapkan Metode Buang di MSDN.
sumber
Dispose
akan dipanggil. Juga, jika objek Anda berpegang pada sumber daya yang langka atau mengunci beberapa sumber daya (misalnya file), maka Anda ingin membebaskannya sesegera mungkin. Menunggu GC melakukan itu adalah suboptimal.might
panggilan, tapiwill
panggilan.Jika mereka mengimplementasikan antarmuka IDisposable maka Anda harus membuangnya. Pengumpul sampah akan mengurus sisanya.
EDIT: terbaik adalah menggunakan
using
perintah saat bekerja dengan barang sekali pakai:sumber
Ketika suatu objek mengimplementasikan
IDisposable
Anda harus memanggilDispose
(atauClose
, dalam beberapa kasus, itu akan memanggil Buang untuk Anda).Anda biasanya tidak harus mengatur objek
null
, karena GC akan tahu bahwa objek tidak akan digunakan lagi.Ada satu pengecualian ketika saya mengatur objek
null
. Ketika saya mengambil banyak objek (dari database) yang perlu saya kerjakan, dan menyimpannya dalam koleksi (atau array). Ketika "pekerjaan" selesai, saya mengatur objek kenull
, karena GC tidak tahu saya selesai bekerja dengannya.Contoh:
sumber
Biasanya, tidak perlu mengatur bidang ke nol. Saya selalu merekomendasikan membuang sumber daya yang tidak dikelola.
Dari pengalaman saya juga menyarankan Anda untuk melakukan hal berikut:
Saya telah menemukan beberapa hal yang sangat sulit untuk menemukan masalah yang merupakan akibat langsung dari tidak mengikuti saran di atas.
Tempat yang baik untuk melakukan ini adalah di Buang (), tetapi lebih cepat biasanya lebih baik.
Secara umum, jika ada referensi ke suatu objek pengumpul sampah (GC) dapat mengambil beberapa generasi lebih lama untuk mengetahui bahwa suatu objek tidak lagi digunakan. Sementara objek tetap dalam memori.
Itu mungkin tidak menjadi masalah sampai Anda menemukan bahwa aplikasi Anda menggunakan lebih banyak memori daripada yang Anda harapkan. Ketika itu terjadi, sambungkan profiler memori untuk melihat benda apa yang tidak dibersihkan. Menyetel bidang yang merujuk objek lain ke null dan menghapus koleksi yang ada dapat benar-benar membantu GC mengetahui objek apa yang dapat dihapus dari memori. GC akan mendapatkan kembali memori yang digunakan lebih cepat membuat aplikasi Anda jauh lebih sedikit memori yang lapar dan lebih cepat.
sumber
Selalu panggil buang. Itu tidak sebanding dengan risikonya. Aplikasi perusahaan yang dikelola besar harus diperlakukan dengan hormat. Tidak ada asumsi yang dapat dibuat atau hal itu akan kembali menggigit Anda.
Jangan dengarkan leppie.
Banyak objek tidak benar-benar menerapkan IDisposable, jadi Anda tidak perlu khawatir tentang mereka. Jika mereka benar-benar keluar dari ruang lingkup mereka akan dibebaskan secara otomatis. Juga saya tidak pernah menemukan situasi di mana saya harus menetapkan sesuatu ke nol.
Satu hal yang bisa terjadi adalah banyak benda bisa dipegang terbuka. Ini dapat sangat meningkatkan penggunaan memori aplikasi Anda. Terkadang sulit untuk mengetahui apakah ini sebenarnya kebocoran memori, atau apakah aplikasi Anda hanya melakukan banyak hal.
Alat profil memori dapat membantu dengan hal-hal seperti itu, tetapi itu bisa rumit.
Selain itu selalu berhenti berlangganan dari acara yang tidak diperlukan. Berhati-hatilah dengan pengikatan dan kontrol WPF. Bukan situasi yang biasa, tapi saya menemukan situasi di mana saya memiliki kontrol WPF yang terikat pada objek yang mendasarinya. Objek yang mendasarinya besar dan memakan banyak memori. Kontrol WPF sedang diganti dengan contoh baru, dan yang lama masih berkeliaran untuk beberapa alasan. Ini menyebabkan kebocoran memori yang besar.
Di situs belakang kode ditulis dengan buruk, tetapi intinya adalah Anda ingin memastikan bahwa hal-hal yang tidak digunakan keluar dari ruang lingkup. Yang satu membutuhkan waktu lama untuk menemukan dengan profiler memori karena sulit untuk mengetahui hal-hal apa dalam memori yang valid, dan apa yang seharusnya tidak ada.
sumber
Saya harus menjawab juga. JIT menghasilkan tabel bersama dengan kode dari analisis statis penggunaan variabel. Entri tabel tersebut adalah "GC-Roots" dalam bingkai tumpukan saat ini. Saat penunjuk instruksi maju, entri tabel tersebut menjadi tidak valid dan siap untuk pengumpulan sampah. Oleh karena itu: Jika itu adalah variabel scoped, Anda tidak perlu mengaturnya ke nol - GC akan mengumpulkan objek. Jika itu anggota atau variabel statis, Anda harus mengaturnya ke nol
sumber