Вызов метода асинхронно несколько раз приводит к исключению OutOfMemory - PullRequest
0 голосов
/ 26 ноября 2010

Проблема: если я вызываю LoadFile () несколько раз (достаточно 10-20 раз) асинхронно, используя большой файл PDF (50 МБ, 1500 страниц), тогда я получаю исключение OutOfMemory довольно быстро. Если я вызываю GC.Collect () после EndInvoke (), то это решает проблему.

Синхронный вызов работает отлично (утечки памяти не происходит).

Есть идеи, как решить эту проблему, не вызывая 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 ]

1 голос
/ 26 ноября 2010

Вероятно, компонент ActiveX бомбит, возвращая E_OUTOFMEMORY.Который переводится в ООМ.Проблема заключается в том, что у вас есть несколько экземпляров этого компонента, когда вы выполняете этот код асинхронно.Для файла PDF объемом 50 МБ потребуется куча неуправляемой памяти, вероятно, несколько сотен мегабайт.

Вызов GC.Collect () работает случайно.Это освобождает ваши массивы fileBytes.Они довольно большие и помещаются в кучу больших объектов.Требуется полный сборщик мусора, чтобы освободить их.То, что делает ваш вызов Collect (), дает компоненту ActiveX некоторое пространство для кражи неуправляемой памяти из диспетчера кучи Windows.

Здесь вы просто нарушаете фундаментальные ограничения памяти 32-битного процесса.Вам нужно будет по крайней мере ограничить количество экземпляров этого компонента, чтобы они не поглотили слишком много памяти.В любом случае многопоточность редко работает с компонентами ActiveX, COM выполняет их вызовы в поток STA.

0 голосов
/ 26 ноября 2010

Не совсем. Своевременное GC.Collect, когда вы знаете, что вам это нужно, это приемлемая практика. Хотя я рекомендую переместить его в конец функции LoadFile (чем ближе к источнику задачи, потребляющей память, тем лучше).

...