多次异步调用方法会导致OutOfMemory异常

时间:2010-11-26 19:25:13

标签: .net memory-leaks asynchronous garbage-collection begininvoke

问题:如果我使用大型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();
    }
}

2 个答案:

答案 0 :(得分:1)

可能是ActiveX组件轰炸,返回E_OUTOFMEMORY。哪个被翻译成OOM。问题是,当您异步运行此代码时,您已经运行了该组件的多个实例。一个50 MB的pdf文件将需要一堆非托管内存,可能需要数百兆字节。

GC.Collect()调用是偶然的。它释放了你的fileBytes数组。它们非常大并且被放入大物体堆中。它需要一个完整的GC来释放它们。您的Collect()调用所做的,为ActiveX组件提供了一些喘息空间,可以从Windows堆管理器中窃取非托管内存。

您只是在这里遇到了32位进程的基本内存限制。您必须至少限制此组件的实例数,以避免让它们吞噬太多内存。无论如何,线程很少适用于ActiveX组件,COM会调用它们对STA线程的调用。

答案 1 :(得分:0)

不是真的。一个很好的定时GC.Collect当你知道你需要它是可以接受的做法。虽然我建议你把它移到LoadFile函数的末尾(越接近消耗任务的内存源越多)。