Bagikan melalui


Menghindari kebocoran memori

Saat menggunakan kontrol Win2D dalam aplikasi XAML terkelola, perawatan harus dilakukan untuk menghindari siklus jumlah referensi yang dapat mencegah kontrol ini pernah direklamasi oleh pengumpul sampah.

Anda memiliki masalah jika...

Jika semua kondisi ini terpenuhi, siklus jumlah referensi akan menjaga kontrol Win2D agar tidak pernah menjadi sampah yang dikumpulkan. Sumber daya Win2D baru dialokasikan setiap kali aplikasi berpindah ke halaman yang berbeda, tetapi yang lama tidak pernah dibeberkan sehingga memori bocor. Untuk menghindari hal ini, Anda harus menambahkan kode untuk secara eksplisit memutus siklus.

Cara memperbaikinya

Untuk memutus siklus jumlah referensi dan membiarkan halaman Anda menjadi sampah yang dikumpulkan:

  • Kaitkan Unloaded peristiwa halaman XAML yang berisi kontrol Win2D
  • Di handler Unloaded , panggil RemoveFromVisualTree kontrol Win2D
  • Unloaded Di handler, rilis (dengan mengatur ke null) referensi eksplisit apa pun ke kontrol Win2D

Contoh kode:

void page_Unloaded(object sender, RoutedEventArgs e)
{
    this.canvas.RemoveFromVisualTree();
    this.canvas = null;
}

Untuk contoh kerja, lihat salah satu halaman demo Galeri Contoh.

Cara menguji kebocoran siklus

Untuk menguji apakah aplikasi Anda melanggar siklus refcount dengan benar, tambahkan metode finalizer ke halaman XAML apa pun yang berisi kontrol Win2D:

~MyPage()
{
    System.Diagnostics.Debug.WriteLine("~" + GetType().Name);
}

Di konstruktor Anda App , siapkan timer yang akan memastikan pengumpulan sampah terjadi secara berkala:

var gcTimer = new DispatcherTimer();
gcTimer.Tick += (sender, e) => { GC.Collect(); };
gcTimer.Interval = TimeSpan.FromSeconds(1);
gcTimer.Start();

Navigasikan ke halaman, lalu menjauhinya ke beberapa halaman lain. Jika semua siklus telah rusak, Anda akan melihat Debug.WriteLine output di panel output Visual Studio dalam satu atau dua detik.

Perhatikan bahwa panggilan GC.Collect mengganggu dan melukai performa, jadi Anda harus menghapus kode pengujian ini segera setelah Anda menyelesaikan pengujian untuk kebocoran!

Detail gory

Siklus terjadi ketika objek A memiliki referensi ke B, pada saat yang sama dengan B juga memiliki referensi ke A. Atau saat A mereferensikan B, dan B mereferensikan C, sementara C mereferensikan A, dll.

Saat berlangganan peristiwa kontrol XAML, siklus semacam ini cukup banyak tidak dapat dihindari:

  • Halaman XAML menyimpan referensi ke semua kontrol yang terkandung di dalamnya
  • Kontrol menyimpan referensi ke delegasi handler yang telah berlangganan acara mereka
  • Setiap delegasi menyimpan referensi ke instans targetnya
  • Penanganan aktivitas biasanya merupakan metode instans dari kelas halaman XAML, sehingga referensi instans target mereka menunjuk kembali ke halaman XAML, membuat siklus

Jika semua objek yang terlibat diimplementasikan dalam .NET, siklus tersebut tidak menjadi masalah karena .NET adalah sampah yang dikumpulkan, dan algoritma pengumpulan sampah dapat mengidentifikasi dan mengklaim kembali kelompok objek bahkan jika ditautkan dalam siklus.

Tidak seperti .NET, C++ mengelola memori dengan penghitungan referensi, yang tidak dapat mendeteksi dan mengklaim kembali siklus objek. Terlepas dari batasan ini, aplikasi C++ yang menggunakan Win2D tidak memiliki masalah karena penanganan aktivitas C++ default untuk menyimpan referensi yang lemah daripada referensi yang kuat ke instans target mereka. Oleh karena itu halaman mereferensikan kontrol, dan kontrol mereferensikan delegasi penanganan aktivitas, tetapi delegasi ini tidak mereferensikan kembali ke halaman sehingga tidak ada siklus.

Masalahnya adalah ketika komponen C++ WinRT seperti Win2D digunakan oleh aplikasi .NET:

  • Halaman XAML adalah bagian dari aplikasi, jadi menggunakan pengumpulan sampah
  • Kontrol Win2D diimplementasikan di C++, jadi menggunakan penghitungan referensi
  • Delegasi penanganan aktivitas adalah bagian dari aplikasi, jadi menggunakan pengumpulan sampah dan menyimpan referensi yang kuat ke instans targetnya

Siklus ada, tetapi objek Win2D yang berpartisipasi dalam siklus ini tidak menggunakan pengumpulan sampah .NET. Ini berarti pengumpul sampah tidak dapat melihat seluruh rantai, sehingga tidak dapat mendeteksi atau mengklaim kembali objek. Ketika ini terjadi, aplikasi harus membantu dengan secara eksplisit memutus siklus. Ini dapat dilakukan baik dengan merilis semua referensi dari halaman ke kontrol (seperti yang direkomendasikan di atas) atau dengan merilis semua referensi dari kontrol ke delegasi penanganan aktivitas yang mungkin menunjuk kembali ke halaman (menggunakan halaman Kejadian yang Tidak Dimuat untuk berhenti berlangganan semua penanganan aktivitas).