Отладка OoM Exception - PullRequest
       7

Отладка OoM Exception

2 голосов
/ 20 марта 2012

Редактировать: Добавлен код (Исключение в строке 095, 5-й раз, когда он нажал).

    public DataTable ParseBarcodes(String[] files, BarcodeZoneScan[] scanParameters)
    {
        message = null;
        //gmseBitmap img = null;
        gmseBitmap rotImg = null;
        gmseBitmap parseImage = null;
        gmseBitmap tempImage = null;

        DataTable codes = new DataTable();
        codes.Columns.Add("PageNumber");
        codes.Columns.Add("Text");
        codes.Columns.Add("Type");
        codes.Columns.Add("RegionName");

        try
        {
            gmseBarcodeInfoCollection bcc;
            gmseBarcodeReaderParameter param = new gmseBarcodeReaderParameter();
            gmseLicense.License = "plaintext license key ommited";

            String dvImageName;

            int searchCount = 0;

            for (int dvCount = 0; dvCount < files.Length; dvCount++)
            {
                if (cancelled) //If cancelled, end the loops
                {
                    dvCount = files.Length;
                    break;
                }

                dvImageName = files[dvCount].ToString();
                using (gmseBitmap img = new gmseBitmap(dvImageName))
                {

                int framecount = img.GetFrameCount();

                for (int e = 0; e < framecount; e++)
                {
                    for (int j = 0; j < scanParameters.Length; j++)
                    {
                        if (scanParameters[j].Range == PageRange.All ||//All 
                            (scanParameters[j].Range == PageRange.Even && (searchCount == 0 || searchCount % 2 == 0)) || //even
                            (scanParameters[j].Range == PageRange.Odd && (searchCount != 0 && searchCount % 2 != 0)) ||
                            (scanParameters[j].Range == PageRange.First && searchCount == 0))
                        {
                            //Setup what barcodes are going to be search for

                            param.BarcodeType = 0;
                            if (scanParameters[j].BarcodeTypes == BarcodeType.All) //All
                            {
                                param.BarcodeType = (int)gmseBarcodeType.All;
                            }
                            else
                            {
                                if ((scanParameters[j].BarcodeTypes & BarcodeType.Code39) != 0) //Code 39
                                    param.BarcodeType = param.BarcodeType | (int)gmseBarcodeType.Code39;
                                if ((scanParameters[j].BarcodeTypes & BarcodeType.Code11) != 0) //Code 11
                                    param.BarcodeType = param.BarcodeType | (int)gmseBarcodeType.Code11;
                                if ((scanParameters[j].BarcodeTypes & BarcodeType.Code93) != 0) //Code 93
                                    param.BarcodeType = param.BarcodeType | (int)gmseBarcodeType.Code93;
                                if ((scanParameters[j].BarcodeTypes & BarcodeType.Code128) != 0) //Code 128
                                    param.BarcodeType = param.BarcodeType | (int)gmseBarcodeType.Code128;
                                if ((scanParameters[j].BarcodeTypes & BarcodeType.Ean8) != 0) //EAN 8
                                    param.BarcodeType = param.BarcodeType | (int)gmseBarcodeType.EAN8;
                                if ((scanParameters[j].BarcodeTypes & BarcodeType.Ean13) != 0) // EAN 13
                                    param.BarcodeType = param.BarcodeType | (int)gmseBarcodeType.EAN13;
                                if ((scanParameters[j].BarcodeTypes & BarcodeType.I2of5) != 0) //I2of5
                                    param.BarcodeType = param.BarcodeType | (int)gmseBarcodeType.i2of5;
                            }

                            param.IgnoreCheckSum = 1;
                            param.ReadMode = gmseBarcodeReadMode.WholeBitmap;

                            using (rotImg = new gmseBitmap(img.ExtractFrame(e)))
                            {

                                // do some basic image enhancement for better results
                                rotImg.ChangePixelFormat(System.Drawing.Imaging.PixelFormat.Format32bppArgb);

                                rotImg.SelectActiveFrame(e);

                                if (scanParameters[j].WholePage)
                                {
                                    parseImage = rotImg.ExtractFrame(e);
                                }
                                else
                                {
                                    using (tempImage = rotImg.ExtractFrame(e))
                                    {
                                        Rectangle convertedRect = returnConvertedRectangle(tempImage, scanParameters[j].Dimensions);
                                        if (convertedRect.IntersectsWith(new Rectangle(0, 0, tempImage.Width, tempImage.Height)))
                                        {
                                            //GC.Collect(); //Test so I can see what objects are still alive in dump
                                            parseImage = tempImage.CopyRectangle(convertedRect); //Exception here
                                        }

                                    }
                                }
                            }
                            //rotImg.Dispose();
                            //rotImg = null;

                            if (parseImage != null)
                            {
                                //Now we will apply the image enhancements:
                                if (scanParameters[j].Enhancements != ImageEnhancement.None)
                                {
                                    rotImg = EnhanceImage(parseImage, scanParameters[j].Enhancements);
                                    parseImage.Dispose();
                                    parseImage = null;
                                }

                                if ((scanParameters[j].BarcodeScanDirection & ScanDirection.LeftToRight) != 0 && !cancelled)
                                {
                                    if (parseImage == null)
                                    {
                                        tempImage = new gmseBitmap(rotImg.Image, 1);
                                    }
                                    else
                                    {
                                        tempImage = new gmseBitmap(parseImage.Image, 1);
                                    }

                                    bcc = tempImage.ReadBarcodes(param);
                                    foreach (gmseBarcodeInfo bc in bcc)
                                    {
                                        addBarcode(codes, new object[] { searchCount, bc.Text, gmseBarcodeTypeConvert(bc.BarcodeType), scanParameters[j].ZoneName });
                                    }

                                    tempImage.Dispose();
                                    tempImage = null;
                                }
                                if ((scanParameters[j].BarcodeScanDirection & ScanDirection.RightToLeft) != 0 && !cancelled)
                                {
                                    if (parseImage == null)
                                    {
                                        tempImage = new gmseBitmap(rotImg.Image, 1);
                                    }
                                    else
                                    {
                                        tempImage = new gmseBitmap(parseImage.Image, 1);
                                    }

                                    tempImage.RotateFlip(RotateFlipType.Rotate180FlipNone);

                                    bcc = tempImage.ReadBarcodes(param);
                                    foreach (gmseBarcodeInfo bc in bcc)
                                    {
                                        addBarcode(codes, new object[] { searchCount, bc.Text, gmseBarcodeTypeConvert(bc.BarcodeType), scanParameters[j].ZoneName });
                                    }

                                    tempImage.Dispose();
                                    tempImage = null;
                                }
                                if ((scanParameters[j].BarcodeScanDirection & ScanDirection.TopToBottom) != 0 && !cancelled)
                                {
                                    if (parseImage == null)
                                    {
                                        tempImage = new gmseBitmap(rotImg.Image, 1);
                                    }
                                    else
                                    {
                                        tempImage = new gmseBitmap(parseImage.Image, 1);
                                    }

                                    tempImage.RotateFlip(RotateFlipType.Rotate90FlipNone);

                                    bcc = tempImage.ReadBarcodes(param);
                                    foreach (gmseBarcodeInfo bc in bcc)
                                    {
                                        addBarcode(codes, new object[] { searchCount, bc.Text, gmseBarcodeTypeConvert(bc.BarcodeType), scanParameters[j].ZoneName });
                                    }

                                    tempImage.Dispose();
                                    tempImage = null;
                                }
                                if ((scanParameters[j].BarcodeScanDirection & ScanDirection.BottomToTop) != 0 && !cancelled)
                                {
                                    if (parseImage == null)
                                    {
                                        tempImage = new gmseBitmap(rotImg.Image, 1);
                                    }
                                    else
                                    {
                                        tempImage = new gmseBitmap(parseImage.Image, 1);
                                    }

                                    tempImage.RotateFlip(RotateFlipType.Rotate270FlipNone);

                                    bcc = tempImage.ReadBarcodes(param);
                                    foreach (gmseBarcodeInfo bc in bcc)
                                    {
                                        addBarcode(codes, new object[] { searchCount, bc.Text, gmseBarcodeTypeConvert(bc.BarcodeType), scanParameters[j].ZoneName });
                                    }

                                    tempImage.Dispose();
                                    tempImage = null;
                                }

                                if (parseImage != null)
                                {
                                    parseImage.Dispose();
                                    parseImage = null;
                                }
                                if (rotImg != null)
                                {
                                    rotImg.Dispose();
                                    rotImg = null;
                                }
                            }
                        }
                    }

                    searchCount++;

                    if (cancelled) //If cancelled, end the loops
                    {
                        e = framecount;
                        dvCount = files.Length;
                    }
                }
                } //end using img
                //img.Dispose();
                //img = null;
            }

        }
        catch (Exception ex)
        {
            message = ex.Message;
        }
        finally
        {
            if (img != null)
            {
                img.Dispose();
                img = null;
            }
            if (rotImg != null)
            {
                rotImg.Dispose();
                rotImg = null;
            }
            if (tempImage != null)
            {
                tempImage.Dispose();
                tempImage = null;
            }
            if (parseImage != null)
            {
                parseImage.Dispose();
                parseImage = null;
            }
        }

        if (!String.IsNullOrEmpty(message))
            throw new Exception(message);

        return codes;
    }

Мы используем этот подключаемый модуль GMSE Imaging для помощи при считывании штрих-кодов с распознавания при сканировании, он работает с перекосом, поворачивая изображение на 10 градусов, пока не получит чтение. Обнаружена ошибка, из-за которой при сканировании листов разного размера возникла ошибка.

Я проследил его от нашей основной программы до одной из наших библиотек DLL, где я обнаружил, что он перехватывает исключение OutOfMemoryException.

Исходный формат TIF составляет 300 КБ, но для поворота изображений выполняется достаточное количество копий. (между 4 растровыми изображениями) Однако я проследил за программой и провел мониторинг местных жителей, и кажется, что каждое растровое изображение корректно распределяется и присваивается нулевое значение, прежде чем метод в циклах сбоев.

Я также пытался добавить GC.Collect() в конце моего цикла.

Я на 32-битном компьютере W7, который, как я прочитал, имеет ограничение в 2 ГБ на объект, с обильным количеством оперативной памяти, так что ничего такого не хватает. Я смотрел его в диспетчере задач, и мое использование оперативной памяти меняется только с 1,72 до 1,78 ГБ.

Это было сложно исследовать, так как OoM кажется необычной ошибкой. Мне было интересно, есть ли у кого-нибудь совет по поводу такого исключения? Я не мастер Visual Studio, есть ли простой способ мониторинга ресурсов / использования памяти?

Или знает какие-либо утилиты, которые я могу использовать, чтобы помочь?

Вывод здесь сообщения об ошибке, не уверен, насколько полезны фрагменты кода в этой ситуации ...

System.OutOfMemoryException was caught
Message=Out of memory.
Source=System.Drawing
 StackTrace:
   at System.Drawing.Bitmap.Clone(Rectangle rect, PixelFormat format)
   at gmse.Imaging.gmseBitmap.CopyRectangle(Rectangle r)
   at ImagingInterface.ImagingFunctions.ParseBarcodes(String[] files, BarcodeZoneScan[] scanParameters) in C:\Working\Scan.backup\Global Dlls\v2.6.0.02\ScanGlobalDlls\ImagingInterface\ImagingFunctions.cs:line 632

InnerException:

(в настоящее время читаем больше в GC / Управление памятью http://msdn.microsoft.com/en-us/library/ee851764.aspx)

Работа над шагом этого руководства с использованием SOS-отладчика в окне Immediate с целью определения, сгенерировано ли исключение из управляемого или неуправляемого кода.


Шаги сверху показали, что это проблема с управляемым кодом, так как показан тип исключения из SOS.

Exception object: 39594518
Exception type: System.OutOfMemoryException
Message: <none>
InnerException: <none>
StackTrace (generated):

Heapdump, который я взял , похоже, не является тысячами растровых изображений, как я ожидал. Не уверен на 100%, как интерпретировать дамп, чтобы увидеть, что я могу найти на нем.

Не уверен, куда двигаться отсюда прямо сейчас! (Поиск ..)


редактирование:

Я пытался применить уроки в этом блоге к моей проблеме.

Начато с PerfMon

Perfmon Graph Этот график показывает мою программу от выполнения до того места, где она ловит исключение.

Первые два резких пика возникают после запуска синтаксического анализа отсканированного изображения, последнее выпадение происходит при обнаружении исключения.

Usage Summary

В: Сравните кривые для виртуальных байтов, личных байтов и # байтов во всех кучах, следуют ли они друг за другом или они расходятся? Каково значение #Bytes во всех кучах, расходящихся от? (Как его квартира на моем)

Проверенная память с адресом !summary MEM_IMAGE соответствовал PrivateBytes (113 МБ) в значительной степени.

Q: Куда идет большая часть памяти (какой RegionType)? RegionUsageFree 87,15% RegionUsageIsVAF 5,64% (занято 43,89%) [память выделена через VirtualAlloc] RegionUsageImage 5,54% (занято 43,13%) [Память, сопоставленная с файлом, который является частью исполняемого образа.]

В WinDbg с загруженной SOS я сделал! DumpHeap

//...
7063424c     1201        28824 System.Collections.ArrayList
706228d4      903        28896 System.EventHandler
7062f640     1253        30072 System.RuntimeType
6ec2be78      833        31216 System.Windows.Forms.PropertyStore+IntegerEntry[]
6ec2b0a4      654        34008 System.Windows.Forms.CreateParams
7063547c      318        35472 System.Collections.Hashtable+bucket[]
6ec2aa5c      664        37184 System.Windows.Forms.Control+ControlNativeWindow
70632938      716        40400 System.Int32[]
6c546700       48        49728 System.Data.RBTree`1+Node[[System.Data.DataRow, System.Data]][]
70634944       85        69600 System.Byte[]
6ec2b020      931        85972 System.Windows.Forms.PropertyStore+ObjectEntry[]
6c547758      156       161616 System.Data.RBTree`1+Node[[System.Int32, mscorlib]][]
705e6c28     2107       238912 System.Object[]
00305ce8       18       293480      Free
7062f9ac     5842       301620 System.String
Total 35669 objects

А вот и верхние объекты, занимающие память. Я надеялся, что что-то высунется, как больной большой палец, как огромное количество растровых изображений или что-то в этом роде. Что-нибудь здесь кричит "Я веду себя необычно!" кому-нибудь? (Я пытаюсь исследовать главные из них на предмет подозрительных вещей, но было бы неплохо еще немного сузить круг возможных виновников)

Эта страница (объяснение адреса объяснено) очень помогла.Однако C # - мой первый язык, поэтому у меня нет опыта отладки проблем с памятью.Хотелось бы узнать, нахожусь ли я на правильном пути ( Является ли ГХ проблемой вообще? ), поскольку я не нашел ничего, что давало бы мне какие-либо четкие указания.


Ответ: Проблема возникла в сторонней библиотеке.Я ничего не могу сделать.Обнаружено путем обдумывания и некоторых тестов с урезанным кодом, включающим только метод, выдающий ошибку.

Награда за то, что, как мне показалось, я выучил больше всего.

Ответы [ 3 ]

1 голос
/ 24 марта 2012

Хорошо, добавленная информация помогает.Проблема не в том, что ваша программа использует слишком много памяти, она использует слишком мало .В куче мусора собрано очень мало данных.Это не редкость для программы, которая управляет растровыми изображениями.Класс Bitmap представляет собой очень маленькую оболочку для функций GDI +, он использует только несколько байтов в куче GC.Таким образом, вы можете создать огромное количество растровых изображений, прежде чем заполнять кучу gen # 0 и запускать сборку мусора.Это также видно из Perfmon, вы хотите посмотреть на .NET CLR Memory, счетчик Gen 0 Collections.Здоровая программа запускает коллекцию примерно 10 раз в секунду, когда выполняет работу.

Нельзя получать коллекции - это нормально, но есть что-то еще, что не работает, когда нет коллекций.Поток финализатора никогда не запускается.Финализаторы важны для освобождения неуправляемых ресурсов, кроме памяти.Как дескрипторы операционной системы и любые указатели неуправляемой памяти, содержащиеся в управляемых объектах.Растровые изображения имеют их.

Первое, что нужно сделать, - запустить Taskmgr.exe, вкладка «Процессы».Нажмите «Вид» + «Выбрать столбцы» и отметьте «Дескрипторы», «Объекты пользователя» и «Объекты GDI».Наблюдайте за этими счетчиками во время работы вашей программы.Если вы видите, что один поднимается без ограничений, у вас есть проблема, которая может привести к тому, что GDI + сгенерирует исключение OOM.GDI-объекты являются общей причиной.

Внимательно просмотрите ваш код и убедитесь, что вы вызываете Dispose () для любого изображения или растрового изображения, которое вы больше не используете.Остерегайтесь тонких, например, присваивая свойство Image для PictureBox.Вам придется избавиться от старого, если он не равен нулю.Это, конечно, больно, и слишком легко пропустить.Поэтому используйте простую стратегию, подсчитайте количество созданных вами растровых изображений и, скажем, сотый вызов GC.Collect + GC.WaitForPendingFinalizers (), чтобы запустить коллекцию и очистку финализатора.

0 голосов
/ 26 марта 2012

Вы можете безопасно добавить блок using вокруг переменной img, и с небольшим рефакторингом вы можете сделать то же самое с другими переменными изображения, которые вы объявляете.

Это должно, по крайней мере, сделать код более читабельным и уменьшить возможность забыть добавить его в блок finally; Я могу даже поспособствовать решению проблемы. Похоже, вы вручную удаляете каждый созданный объект изображения.

0 голосов
/ 21 марта 2012

В прошлом я всегда использовал ANTS Memory Profiler для устранения подобных проблем. Это не бесплатно, но работает довольно хорошо для утечек памяти / ссылок в управляемом коде. Вы просто делаете пару снимков, когда приложение должно быть в устойчивом состоянии, и смотрите на изменения.

...