Pertanyaan .NET Memory mengeluarkan pemuatan ~ 40 gambar, memori tidak direklamasi, berpotensi karena fragmentasi LOH


Nah, ini adalah perkelahian pertama saya ke dalam profil memori aplikasi .NET (Penyetelan CPU yang telah saya lakukan) dan saya memukul sedikit dinding di sini.

Saya memiliki pandangan di aplikasi saya yang memuat 40 gambar (maks) per halaman, masing-masing berjalan sekitar ~ 3MB. Jumlah halaman maksimal adalah 10. Melihat karena saya tidak ingin menyimpan 400 gambar atau 1,2GB dalam memori sekaligus, saya mengatur setiap gambar ke nol ketika halaman diubah.

Sekarang, awalnya saya berpikir bahwa saya harus memiliki referensi basi untuk gambar-gambar ini. Saya mengunduh profiler ANTS (alat Bantu BTW) dan menjalankan beberapa tes. Grafik life time memberi tahu saya bahwa saya tidak memiliki referensi apa pun ke gambar-gambar ini selain referensi tunggal di kelas induk (yang berdasarkan desain, juga dikonfirmasi dengan secara cermat menyisir melalui kode saya):

enter image description here

Kelas induk SlideViewModelBase menempel selamanya di cache, tetapi MacroImage properti disetel ke nol saat halaman diubah. Saya tidak melihat indikasi bahwa benda-benda ini harus disimpan lebih lama dari yang diharapkan.

Selanjutnya saya melihat tumpukan objek besar dan penggunaan memori secara umum. Setelah melihat tiga halaman gambar, saya memiliki 691,9MB memori yang tidak dikelola dan 442,3MB pada LOH. System.Byte[], yang berasal dari saya System.Drawing.Bitmap untuk BitmapImage konversi mengambil hampir semua ruang LOH. Ini kode konversi saya:

public static BitmapSource ToBmpSrc( this Bitmap b )
{
    var bi = new BitmapImage();
    var ms = new MemoryStream();
    bi.CacheOption = BitmapCacheOption.OnLoad;
    b.Save( ms,  ImageFormat.Bmp );
    ms.Position = 0;
    bi.BeginInit();
    ms.Seek( 0, SeekOrigin.Begin );
    bi.StreamSource = ms;
    bi.EndInit();
    return bi;
}

Saya mengalami kesulitan untuk menemukan di mana semua memori yang tidak dikelola itu terjadi. Saya menduga itu System.Drawing.Bitmap objek pada awalnya, tetapi ANTS tidak menunjukkan mereka menempel di sekitar, dan saya juga menjalankan tes di mana saya benar-benar yakin bahwa semuanya dibuang dan itu tidak membuat perbedaan. Jadi saya belum tahu dari mana semua memori yang tidak dikelola itu berasal.

Dua teori saya saat ini adalah:

  1. Fragmentasi LOH. Jika saya menavigasi keluar dari tampilan paged dan mengklik beberapa tombol sekitar setengah dari ~ 1,5GB direklamasi. Masih terlalu banyak, tapi tetap menarik.
  2. Beberapa hal yang aneh WPF mengikat. Kami menggunakan penyatuan data untuk menampilkan gambar-gambar ini dan saya bukan ahli dalam hal seluk beluk bagaimana cara kerja kontrol WPF ini.

Jika ada yang punya teori atau profil tips saya akan sangat berterima kasih karena (tentu saja) kami berada di tenggat waktu yang ketat dan saya sedikit berebut untuk menyelesaikan bagian akhir ini dan bekerja. Saya pikir saya sudah dimanjakan dengan melacak kebocoran memori di C ++ ... siapa yang akan 'berpikir?

Jika Anda memerlukan info lebih lanjut atau ingin saya mencoba sesuatu yang lain, silakan bertanya. Maaf tentang wall-o-text di sini, saya mencoba membuatnya sesingkat mungkin.


32
2018-06-07 21:32


asal


Jawaban:


Ini posting blog tampaknya mendeskripsikan apa yang Anda lihat, dan solusi yang diusulkan adalah membuat implementasi Stream yang membungkus aliran lain.

Metode Buang dari kelas pembungkus ini perlu melepaskan aliran yang terbungkus, sehingga bisa menjadi sampah yang dikumpulkan. Setelah BitmapImage dijalankan dengan aliran pembungkus ini, aliran pembungkus dapat dibuang, melepaskan aliran yang mendasari, dan memungkinkan array byte besar itu sendiri untuk dibebaskan.

BitmapImage menyimpan referensi ke aliran sumber sehingga membuat objek MemoryStream tetap hidup. Sayangnya, meskipun MemoryStream.Dispose telah dipanggil, itu tidak melepaskan array byte yang dibungkus aliran memori. Jadi, dalam hal ini, bitmap adalah referensi aliran, yang merujuk buffer, yang mungkin memakan banyak ruang pada tumpukan objek besar. Tidak ada kebocoran memori yang sebenarnya; ketika tidak ada referensi lagi ke bitmap, semua objek ini akan (akhirnya) menjadi sampah yang dikumpulkan. Tetapi karena bitmap telah membuat salinan pribadinya sendiri dari gambar (untuk rendering), tampaknya agak boros untuk memiliki salinan asli bitmap yang masih tidak perlu yang masih dalam memori.

Juga, versi apa dari .NET yang Anda gunakan? Sebelum .NET 3.5 SP1, ada masalah yang diketahui di mana a BitmapImage dapat menyebabkan kebocoran memori. Solusinya adalah menelepon Membekukan pada BitmapImage.


35
2018-06-07 21:45



Di mana Anda menutup dan membuang aliran memori? Bisa jadi bahwa GC harus bekerja lebih keras untuk membebaskan sumber daya dengan menggerakkan beberapa generasi ke atas sebelum mengeksekusi destruktor pada objek (yang biasanya disebut membuang jika Anda lupa melakukannya).

Dalam kasus Anda, Anda tidak dapat membuang aliran memori sampai Anda selesai dengan gambar. Ketika Anda ingin mereka diturunkan, loop melalui gambar dan coba buang aliran memori.


2
2018-06-07 21:41