Bagikan melalui


callbackOnCollectedDelegate MDA

Catatan

Artikel ini khusus untuk .NET Framework. Ini tidak berlaku untuk implementasi .NET yang lebih baru, termasuk .NET 6 dan versi yang lebih baru.

Asisten penelusuran kesalahan terkelola (MDA) callbackOnCollectedDelegate diaktifkan jika delegasi disusun dari kode terkelola ke tak terkelola sebagai pointer fungsi dan panggilan balik ditempatkan pada pointer fungsi tersebut setelah delegasi mengumpulkan sampah.

Gejala

Pelanggaran akses terjadi ketika mencoba memanggil kode terkelola melalui pointer fungsi yang diperoleh dari delegasi terkelola. Kegagalan ini, meskipun bukan bug runtime bahasa umum (CLR), mungkin tampak demikian karena pelanggaran akses terjadi dalam kode CLR.

Kegagalan tidak konsisten; terkadang panggilan pada pointer fungsi berhasil dan terkadang gagal. Kegagalan mungkin terjadi hanya pada beban berat atau pada sejumlah upaya acak.

Penyebab

Delegasi dari tempat pointer fungsi dibuat dan diekspos ke kode tak terkelola adalah sampah yang dikumpulkan. Ketika komponen yang tidak dikelola mencoba memanggil pointer fungsi, hal tersebut menghasilkan pelanggaran akses.

Kegagalan muncul secara acak karena bergantung pada kapan pengumpulan sampah terjadi. Jika delegasi memenuhi syarat untuk pengumpulan, pengumpulan sampah dapat terjadi setelah panggilan balik dan panggilan berhasil. Di lain waktu, pengumpulan sampah terjadi sebelum panggilan balik, panggilan balik menghasilkan pelanggaran akses, dan program berhenti.

Peluang kegagalan bergantung pada waktu antara penyusunan delegasi dan panggilan balik pada pointer fungsi serta frekuensi pengumpulan sampah. Kegagalan ini sporadis jika waktu antara penyusunan delegasi dan panggilan balik berikutnya singkat. Hal ini biasanya terjadi jika metode tidak terkelola yang menerima pointer fungsi tidak menyimpan pointer fungsi untuk digunakan nanti, tetapi segera memanggil kembali pointer fungsi untuk menyelesaikan operasinya sebelum kembali. Demikian pula, lebih banyak pengumpulan sampah terjadi ketika sistem memuat beban berat, yang membuatnya lebih mungkin bahwa pengumpulan sampah akan terjadi sebelum panggilan balik.

Resolusi

Setelah delegasi disusun sebagai pointer fungsi yang tidak dikelola, pengumpul sampah tidak dapat melacak masa pakainya. Sebagai gantinya, kode Anda harus menyimpan referensi ke delegasi selama masa pakai pointer fungsi yang tidak dikelola. Tetapi sebelum Anda dapat melakukan itu, Anda harus terlebih dahulu mengidentifikasi delegasi mana yang dikumpulkan. Jika diaktifkan, MDA menyediakan nama jenis delegasi. Gunakan nama ini untuk mencari kode Anda untuk pemanggilan platform atau tanda tangan COM yang meneruskan delegasi tersebut ke kode tak terkelola. Delegasi yang menyebabkan pelanggaran diteruskan melalui salah satu situs panggilan ini. Anda juga dapat mengaktifkan MDA gcUnmanagedToManaged untuk memaksa pengumpulan sampah sebelum setiap panggilan balik ke runtime. Ini akan menghilangkan ketidakpastian yang ditimbulkan oleh pengumpulan sampah dengan memastikan bahwa pengumpulan sampah selalu terjadi sebelum panggilan balik. Setelah Anda tahu delegasi apa yang dikumpulkan, ubah kode Anda untuk menyimpan referensi ke delegasi tersebut di sisi terkelola selama masa pakai pointer fungsi tak terkelola yang disusun.

Efek pada Runtime

Ketika delegasi disusun sebagai pointer fungsi, runtime mengalokasikan thunk yang melakukan transisi dari tak terkelola menjadi terkelola. Thunk ini adalah apa yang sebenarnya dipanggil kode tak terkelola sebelum delegasi terkelola akhirnya dipanggil. Tanpa MDA callbackOnCollectedDelegate diaktifkan, kode penyusunan yang tidak dikelola dihapus setelah delegasi dikumpulkan. Dengan MDA callbackOnCollectedDelegate diaktifkan, kode penyusunan yang tidak dikelola tidak segera dihapus setelah delegasi dikumpulkan. Sebaliknya, 1.000 instans terakhir tetap aktif secara default dan diubah untuk mengaktifkan MDA saat dipanggil. Thunk akhirnya dihapus setelah lebih dari 1.001 delegasi yang disusun dikumpulkan.

Output

MDA melaporkan nama jenis delegasi yang dikumpulkan sebelum panggilan balik dicoba pada pointer fungsi yang tidak dikelola.

Konfigurasi

Contoh berikut menunjukkan opsi konfigurasi aplikasi. Ini menetapkan jumlah thunk MDA tetap aktif menjadi 1.500. Nilai listSize default adalah 1.000, minimalnya 50, dan maksimalnya 2.000.

<mdaConfig>
  <assistants>
    <callbackOnCollectedDelegate listSize="1500" />
  </assistants>
</mdaConfig>

Contoh

Contoh berikut menunjukkan situasi yang dapat mengaktifkan MDA ini:

// Library.cpp : Defines the unmanaged entry point for the DLL application.
#include "windows.h"
#include "stdio.h"

void (__stdcall *g_pfTarget)();

void __stdcall Initialize(void __stdcall pfTarget())
{
    g_pfTarget = pfTarget;
}

void __stdcall Callback()
{
    g_pfTarget();
}
// C# Client
using System;
using System.Runtime.InteropServices;

public class Entry
{
    public delegate void DCallback();

    public static void Main()
    {
        new Entry();
        Initialize(Target);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Callback();
    }

    public static void Target()
    {
    }

    [DllImport("Library", CallingConvention = CallingConvention.StdCall)]
    public static extern void Initialize(DCallback pfDelegate);

    [DllImport ("Library", CallingConvention = CallingConvention.StdCall)]
    public static extern void Callback();

    ~Entry() { Console.Error.WriteLine("Entry Collected"); }
}

Lihat juga