0

問題: 大きな PDF ファイル (50Mb、1500 ページ) を使用して非同期に LoadFile() を数回 (10 ~ 20 回で十分) 呼び出すと、すぐに OutOfMemory 例外が発生します。EndInvoke() の後に GC.Collect() を呼び出すと、問題が解決します。

同期呼び出しはうまく機能します (メモリ リークは発生しません)。

GC.Collect() を直接呼び出さずに解決する方法についてのアイデアはありますか?

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Open_Click(object sender, EventArgs e)
    {
        MethodInvoker invoker = this.LoadFile;
        AsyncCallback callback = CallBack;

        invoker.BeginInvoke(callback, null);

        // Synchronous call.
        // LoadFile();
    }

    private void CallBack(IAsyncResult ar)
    {
        AsyncResult result = (AsyncResult)ar;

        MethodInvoker invoker = (MethodInvoker)result.AsyncDelegate;
        invoker.EndInvoke(ar);

        // GC.Collect();
    }

    private void LoadFile()
    {
        byte[] fileBytes = File.ReadAllBytes(@"c:\50mb.pdf");

        // Third party OCX component for viewing PDF files.
        this.pdfOcxViewer.OpenBuffer(fileBytes, fileBytes.Length, "");
        this.pdfOcxViewer.CloseFile();
    }
}
4

2 に答える 2

1

爆撃してE_OUTOFMEMORYを返すのはおそらくActiveXコンポーネントです。これはOOMに変換されます。問題は、このコードを非同期で実行すると、そのコンポーネントの複数のインスタンスが実行されることです。50 MBのPDFファイルには、おそらく数百メガバイトの大量のアンマネージメモリが必要になります。

GC.Collect()呼び出しは誤って機能します。fileBytes配列を解放します。それらは非常に大きく、ラージオブジェクトヒープに配置されます。それらをリリースするには、完全なGCが必要です。Collect()呼び出しは、ActiveXコンポーネントに、Windowsヒープマネージャーからアンマネージメモリを盗むための余裕を与えます。

ここでは、32ビットプロセスの基本的なメモリ制限に直面しています。少なくともこのコンポーネントのインスタンスの数を制限して、メモリを大量に消費しないようにする必要があります。とにかく、スレッディングがActiveXコンポーネントで機能することはめったになく、COMはSTAスレッドへの呼び出しをマーシャリングします。

于 2010-11-26T19:48:29.727 に答える
0

Not really. A well timed GC.Collect when you know you need it is acceptable practice. Although I recommend you move it to the end of LoadFile function (the closer to the source of memory consuming task the better).

于 2010-11-26T19:35:20.010 に答える