Strategi dan alat apa yang berguna untuk menemukan kebocoran memori di .NET?

152

Saya menulis C ++ selama 10 tahun. Saya mengalami masalah memori, tetapi bisa diperbaiki dengan upaya yang wajar.

Selama beberapa tahun terakhir saya telah menulis C #. Saya menemukan masih banyak masalah memori. Mereka sulit untuk didiagnosis dan diperbaiki karena tidak adanya determinasi, dan karena filosofi C # adalah bahwa Anda tidak perlu khawatir tentang hal-hal seperti itu ketika Anda benar-benar melakukannya.

Satu masalah khusus yang saya temukan adalah bahwa saya harus membuang dan membersihkan segala sesuatu dalam kode secara eksplisit. Jika saya tidak melakukannya, maka profiler memori tidak benar-benar membantu karena ada begitu banyak sekam melayang tentang Anda tidak dapat menemukan kebocoran di dalam semua data yang mereka coba tunjukkan kepada Anda. Saya ingin tahu apakah saya mendapat ide yang salah, atau apakah alat yang saya miliki bukan yang terbaik.

Strategi dan alat apa yang berguna untuk mengatasi kebocoran memori di .NET?

Scott Langham
sumber
Judul posting Anda tidak benar-benar cocok dengan pertanyaan di posting Anda. Saya sarankan Anda memperbarui judul Anda.
Kevin
Kamu benar. Maaf, saya mulai muak dengan kebocoran saat ini yang saya buru! Judul diperbarui.
Scott Langham
3
@Scott: Jangan muak dengan .NET, bukan itu masalahnya. Kode Anda adalah.
GEOCHET
3
Yap, kode saya, atau perpustakaan pihak ketiga yang saya senang gunakan.
Scott Langham
@Scott: Lihat jawaban saya. MemProfiler sepadan. Menggunakannya juga akan memberi Anda tingkat pemahaman yang sepenuhnya baru tentang dunia .NET GC.
GEOCHET

Jawaban:

51

Saya menggunakan MemProfiler Scitech ketika saya mencurigai ada kebocoran memori.

Sejauh ini, saya merasa sangat andal dan kuat. Ini telah menghemat daging saya setidaknya pada satu kesempatan.

GC berfungsi dengan sangat baik dalam .NET IMO, tetapi sama seperti bahasa atau platform lainnya, jika Anda menulis kode yang buruk, hal-hal buruk terjadi.

GEOCHET
sumber
3
Yap, saya sudah mencoba yang ini, dan itu membantu saya sampai pada bagian bawah dari beberapa kebocoran yang rumit. Kebocoran terbesar saya ternyata disebabkan oleh perpustakaan pihak ketiga dalam kode yang tidak dikelola yang mereka akses melalui interop. Saya terkesan bahwa alat ini mendeteksi kebocoran dalam kode yang tidak dikelola serta kode yang dikelola.
Scott Langham
1
Saya telah menerima ini sebagai jawabannya karena pada akhirnya itulah yang berhasil bagi saya, tetapi saya pikir semua jawaban lainnya sangat berguna. Omong-omong, alat ini lebih umum disebut Mem Profiler SciTech!
Scott Langham
41

Hanya untuk masalah pelepasan-untuk-buang, coba solusi yang dijelaskan dalam posting blog ini . Inilah intinya:

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif
Jay Bazuzi
sumber
Saya lebih suka melempar pengecualian daripada Debug. Gagal
Pedro77
17

Kami telah menggunakan Ants Profiler Pro oleh perangkat lunak Red Gate di proyek kami. Ini bekerja sangat baik untuk semua aplikasi berbasis bahasa .NET.

Kami menemukan bahwa .NET Garbage Collector sangat "aman" dalam membersihkan objek dalam memori (sebagaimana mestinya). Itu akan menjaga benda-benda di sekitar hanya karena kita mungkin menggunakannya di masa depan. Ini berarti kami harus lebih berhati-hati dengan jumlah objek yang kami gembungkan dalam memori. Pada akhirnya, kami mengubah semua objek data kami menjadi "mengembang saat diminta" (tepat sebelum bidang diminta) untuk mengurangi overhead memori dan meningkatkan kinerja.

EDIT: Berikut adalah penjelasan lebih lanjut tentang apa yang saya maksud dengan "mengembang pada permintaan." Dalam model objek kami dari basis data kami, kami menggunakan properti dari objek induk untuk mengekspos objek anak. Sebagai contoh jika kita memiliki beberapa catatan yang mereferensikan catatan "detail" atau "pencarian" lainnya pada basis satu-ke-satu, kita akan menyusunnya seperti ini:

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

Kami menemukan bahwa sistem di atas menciptakan beberapa masalah memori dan kinerja nyata ketika ada banyak catatan dalam memori. Jadi kami beralih ke sistem di mana objek digembungkan hanya ketika diminta, dan panggilan basis data dilakukan hanya jika diperlukan:

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

Ini ternyata jauh lebih efisien karena benda-benda disimpan keluar dari memori sampai mereka diperlukan (metode Dapatkan diakses). Ini memberikan peningkatan kinerja yang sangat besar dalam membatasi hit basis data dan keuntungan besar pada ruang memori.

Menandai
sumber
Saya yang kedua produk ini. Itu adalah salah satu profiler terbaik yang saya gunakan.
Gord
Saya menemukan profiler cukup baik untuk melihat masalah kinerja. Namun, alat analisis memori sangat buruk. Saya menemukan kebocoran dengan alat ini, tetapi itu sampah untuk membantu saya mengidentifikasi penyebab kebocoran. Dan itu sama sekali tidak membantu Anda jika kebocoran terjadi pada kode yang tidak dikelola.
Scott Langham
Ok, versi baru 5.1, jauh lebih baik. Lebih baik membantu Anda menemukan penyebab kebocoran (meskipun - masih ada beberapa masalah dengan itu yang SEMUT katakan kepada saya mereka akan memperbaikinya di versi berikutnya). Tetap tidak melakukan kode yang tidak dikelola, tetapi jika Anda tidak terganggu dengan kode yang tidak dikelola, ini sekarang merupakan alat yang cukup bagus.
Scott Langham
7

Anda masih perlu khawatir tentang memori ketika Anda menulis kode terkelola kecuali aplikasi Anda sepele. Saya akan menyarankan dua hal: pertama, baca CLR via C # karena itu akan membantu Anda memahami manajemen memori dalam .NET. Kedua, belajar menggunakan alat seperti CLRProfiler (Microsoft). Ini dapat memberi Anda gambaran tentang apa yang menyebabkan kebocoran memori Anda (misalnya, Anda dapat melihat fragmentasi tumpukan objek besar Anda)

Zac Gochenour
sumber
Ya. CLRPRofiler sangat keren. Itu bisa menjadi sedikit eksplosif dengan informasi ketika mencoba menggali melalui tampilan itu memberi Anda objek yang dialokasikan, tetapi semuanya ada di sana. Ini jelas merupakan titik awal yang baik, terutama karena gratis.
Scott Langham
6

Apakah Anda menggunakan kode yang tidak dikelola? Jika Anda tidak menggunakan kode yang tidak dikelola, menurut Microsoft, kebocoran memori dalam pengertian tradisional tidak dimungkinkan.

Memori yang digunakan oleh suatu aplikasi mungkin tidak dirilis, jadi alokasi memori aplikasi dapat tumbuh sepanjang umur aplikasi.

Dari Cara mengidentifikasi kebocoran memori dalam runtime bahasa umum di Microsoft.com

Kebocoran memori dapat terjadi dalam aplikasi .NET Framework ketika Anda menggunakan kode yang tidak dikelola sebagai bagian dari aplikasi. Kode yang tidak dikelola ini dapat membocorkan memori, dan runtime .NET Framework tidak dapat mengatasi masalah itu.

Selain itu, sebuah proyek hanya tampak memiliki kebocoran memori. Kondisi ini dapat terjadi jika banyak objek besar (seperti objek DataTable) dideklarasikan dan kemudian ditambahkan ke koleksi (seperti DataSet). Sumber daya yang dimiliki benda-benda ini mungkin tidak pernah dilepaskan, dan sumber daya dibiarkan hidup untuk keseluruhan program. Tampaknya ini adalah kebocoran, tetapi sebenarnya itu hanya gejala dari cara memori dialokasikan dalam program.

Untuk menangani masalah jenis ini, Anda dapat menerapkan IDisposable . Jika Anda ingin melihat beberapa strategi untuk menangani manajemen memori, saya sarankan mencari IDisposable, XNA, manajemen memori karena pengembang game harus memiliki pengumpulan sampah yang lebih dapat diprediksi dan karenanya harus memaksa GC untuk melakukan hal tersebut.

Satu kesalahan umum adalah tidak menghapus event handler yang berlangganan suatu objek. Berlangganan event handler akan mencegah objek didaur ulang. Juga, lihat pernyataan menggunakan yang memungkinkan Anda untuk membuat ruang lingkup terbatas untuk seumur hidup sumber daya.

Timothy Lee Russell
sumber
5
Lihat blogs.msdn.com/tess/archive/2006/01/23/… . Tidak masalah apakah kebocoran memori "tradisional" atau tidak, itu masih bocor.
Constantin
2
Saya mengerti maksud Anda - tetapi alokasi dan penggunaan kembali memori yang tidak efisien oleh suatu program berbeda dari kebocoran memori.
Timothy Lee Russell
jawaban yang bagus, terima kasih telah mengingat saya bahwa penangan acara bisa berbahaya.
frameworkninja
3
@Timothy Lee Russel: Jika jumlah memori yang tidak terbatas (1) dapat tetap dialokasikan secara bersamaan (di-root) setelah menjadi tidak berguna (2), tanpa sistem apa pun yang memiliki informasi dan dorongan yang diperlukan untuk mencabutnya secara tepat waktu, itu adalah kebocoran memori . Bahkan jika memori mungkin dibebaskan suatu hari nanti, jika barang-barang tidak berguna yang cukup menumpuk untuk mencekik sistem sebelum itu terjadi, itu adalah kebocoran. (1) Lebih besar dari O (N), N menjadi jumlah alokasi yang berguna; (2) Barang tidak berguna jika menghapus referensi tidak akan memengaruhi fungsionalitas program.
supercat
2
@Timothy Lee Russel: Pola "kebocoran memori" normal terjadi ketika memori dipegang oleh satu entitas atas nama entitas lain , berharap untuk diberitahu ketika itu tidak lagi diperlukan, tetapi yang terakhir meninggalkan entitas tanpa memberi tahu yang pertama. Entitas yang memegang memori tidak benar-benar membutuhkannya, tetapi tidak ada cara untuk menentukannya.
supercat
5

Blog ini memiliki beberapa langkah mudah menggunakan windbg dan alat lain untuk melacak kebocoran memori dari semua jenis. Bacaan yang bagus untuk mengembangkan keterampilan Anda.

twk
sumber
5

Saya baru saja mengalami kebocoran memori di layanan windows, yang saya perbaiki.

Pertama, saya mencoba MemProfiler . Saya merasa sangat sulit digunakan dan sama sekali tidak ramah pengguna.

Lalu, saya menggunakan JustTrace yang lebih mudah digunakan dan memberi Anda lebih banyak detail tentang objek yang tidak dibuang dengan benar.

Itu memungkinkan saya untuk memecahkan kebocoran memori dengan sangat mudah.

billybob
sumber
3

Jika kebocoran yang Anda amati adalah karena implementasi cache yang tidak terkendali, ini adalah skenario di mana Anda mungkin ingin mempertimbangkan penggunaan WeakReference. Ini bisa membantu memastikan bahwa memori dilepaskan ketika diperlukan.

Namun, IMHO akan lebih baik untuk mempertimbangkan solusi yang dipesan lebih dahulu - hanya Anda yang benar-benar tahu berapa lama Anda perlu menyimpan benda-benda di sekitarnya, jadi merancang kode tata graha yang sesuai untuk situasi Anda biasanya merupakan pendekatan terbaik.

Chris Ballard
sumber
3

Saya lebih suka dotmemory dari Jetbrains

josepainumkal
sumber
Anda mungkin satu-satunya :)
HellBaby
Saya mencobanya juga. Saya pikir ini adalah alat yang bagus. Mudah digunakan, informatif. Terintegrasi dengan Visual Studio
redeye
Dalam kasus kami, ketika pemecahan masalah kebocoran memori, alat Visual Studio Snapshot jatuh / tidak snapshot. Dotmemory tetap tenang dan menangani beberapa snapshot 3+ GB dengan (tampaknya) mudah.
Michael Kargl
3

Pistol besar - Alat Debugging untuk Windows

Ini adalah koleksi alat yang luar biasa. Anda dapat menganalisis tumpukan yang dikelola dan yang tidak dikelola dengan itu dan Anda dapat melakukannya secara offline. Ini sangat berguna untuk men-debug salah satu aplikasi ASP.NET kami yang terus melakukan daur ulang karena terlalu banyak menggunakan memori. Saya hanya harus membuat dump memori penuh dari proses hidup yang berjalan di server produksi, semua analisis dilakukan secara offline di WinDbg. (Ternyata beberapa pengembang terlalu sering menggunakan penyimpanan Sesi dalam memori.)

"Jika rusak itu ..." blog memiliki artikel yang sangat berguna tentang masalah ini.

Konstantin
sumber
2

Hal terbaik untuk diingat adalah untuk melacak referensi ke objek Anda. Sangat mudah untuk berakhir dengan menggantung referensi ke objek yang tidak Anda pedulikan lagi. Jika Anda tidak akan menggunakan sesuatu lagi, singkirkan itu.

Biasakan menggunakan penyedia cache dengan geser masa kadaluwarsa, sehingga jika sesuatu tidak direferensikan untuk jendela waktu yang diinginkan itu dereferenced dan dibersihkan. Tetapi jika sedang diakses banyak itu akan mengatakan dalam memori.

Gord
sumber
2

Salah satu alat terbaik adalah menggunakan Alat Debugging untuk Windows , dan mengambil dump memori dari proses menggunakan adplus , kemudian menggunakan windbg dan plugin sos untuk menganalisis memori proses, utas, dan tumpukan panggilan.

Anda dapat menggunakan metode ini untuk mengidentifikasi masalah di server juga, setelah menginstal alat, berbagi direktori, lalu menyambung ke berbagi dari server menggunakan (penggunaan net) dan mengambil crash atau hang dump proses.

Kemudian analisa secara offline.

Stuart McConnell
sumber
Ya, ini bekerja dengan baik, terutama untuk hal-hal yang lebih maju atau mendiagnosis masalah dalam perangkat lunak yang dirilis yang Anda tidak dapat dengan mudah melampirkan debugger. Blog ini memiliki banyak tips tentang cara menggunakan alat-alat ini dengan baik: blogs.msdn.com/tess
Scott Langham
2

Setelah salah satu perbaikan saya untuk aplikasi terkelola, saya memiliki hal yang sama, seperti cara memverifikasi bahwa aplikasi saya tidak akan memiliki kebocoran memori yang sama setelah perubahan saya berikutnya, jadi saya telah menulis sesuatu seperti kerangka Verifikasi Rilis Objek, silakan lihat paket NuGet ObjectReleaseVerification . Anda dapat menemukan sampel di sini https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample , dan informasi tentang sampel ini http://outcoldman.ru/en/blog/show/322

outcoldman
sumber
0

Dari Visual Studio 2015 pertimbangkan untuk menggunakan di luar kotak alat diagnostik Penggunaan Memori untuk mengumpulkan dan menganalisis data penggunaan memori.

Alat Penggunaan Memori memungkinkan Anda mengambil satu atau lebih snapshot dari tumpukan memori yang dikelola dan asli untuk membantu memahami dampak penggunaan memori dari jenis objek.

Michael Freidgeim
sumber
0

salah satu alat terbaik yang saya gunakan DotMemory. Anda dapat menggunakan alat ini sebagai ekstensi dalam VS.setelah menjalankan aplikasi Anda, Anda dapat menganalisis setiap bagian dari memori (berdasarkan Object, NameSpace, dll) yang digunakan aplikasi Anda dan mengambil snapshot dari itu , Bandingkan dengan SnapShots lainnya. DotMemory

Rebwar
sumber