调试OoM异常

时间:2012-03-20 13:56:10

标签: c# debugging out-of-memory sos

编辑:添加了代码(第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插件来帮助OCR从扫描中读取条形码,它通过将图像旋转10度直到读取来处理歪斜。发现了一个错误,扫描不同大小的工作表会引发错误。

我将它从我们的主程序跟踪到我们的一个DLL,在那里我发现它正在捕获OutOfMemoryException。

原始TIF为300kb,但是为了旋转图像进行了大量的复制。 (4位图之间) 但是我已经跟踪了程序并监视了本地,并且在故障循环的方法之前,似乎正在处理每个位图并正确分配null。

我也尝试在循环结束时添加GC.Collect()

我使用的是32位W7机器,我读过这个机器每个对象有2GB的限制,有大量的RAM,所以没有什么不那么尊重。 在任务管理器上看它,我的RAM使用率仅从1.72GB到1.78GB。

这是一个棘手的研究,因为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调试器处理step of this guide,目的是确定是否从托管代码或非托管代码生成异常。


上面的步骤表明托管代码存在问题,因为显示了SOS的异常类型。

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

The Heapdump I took似乎没有像我预期的那样成千上万的位图。不是100%确定如何解释转储,以便看到我能在其上找到的内容。

现在不知道从哪里搬到这里! (搜索..)


修改

我一直在努力将this blog中的课程应用到我的问题中。

开始使用PerfMon

Perfmon Graph 此图显示了我的程序从执行到捕获异常的位置。

在触发解析扫描图像后出现前两个尖峰,捕捉到异常时会发生最后一次下降。

Usage Summary

  

问:比较所有堆中的虚拟字节,私有字节和#Bytes的曲线,它们是否相互关注还是分歧?   #Bytes在所有堆中的重要性是什么? (作为我的公寓)

使用!address -summary 检查内存 MEM_IMAGE相当于PrivateBytes(113MB)。

  问:大部分内存在哪里(哪个RegionType)?   RegionUsage免费87.15%   RegionUsageIsVAF 5.64%(繁忙43.89%)[通过VirtualAlloc分配的内存]   RegionUsageImage 5.54%(忙43.13%)[映射到属于可执行映像的文件的内存。]

在加载了SOS的WinDbg中,我做了一个!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

以下是占用内存最多的内存。 我希望有些东西会像拇指一样疼痛,就像巨大的位图或东西一样。这里有什么东西尖叫出来“我表现异乎寻常!”对任何人? (我试图单独检查那些可疑的东西,但是可以更好地缩小可能的罪魁祸首)

This page (Address summary explained)给了我很大的帮助。 但是C#是我的第一语言,所以我以前没有调试内存问题的经验。想知道我是否走在正确的轨道上( GC是一个问题吗?),因为我还没有找到任何给我任何明确迹象的东西。


答:问题是由第三方库引起的。我无能为力。 通过审议和一些测试发现,其中包含仅涉及产生错误的方法的精简代码。

Bounty奖励了我从中学到的最多。

3 个答案:

答案 0 :(得分:1)

好的,添加的信息有帮助。问题不在于您的程序使用了太多内存,而是使用太少。垃圾收集堆中的数据非常少。对于操作位图的程序来说,这并不罕见。 Bitmap类是GDI +函​​数的一个非常小的包装器,它只使用GC堆中的少量字节。因此,在填充gen#0堆并触发垃圾收集之前,您可以创建大量的位图。这也可以从Perfmon中看到,你想看一下.NET CLR Memory,Gen 0 Collections计数器。一个健康的程序在工作时每秒触发一次大约10次的收集。

没有收藏品很好,但是当没有收藏品时,还有其它东西不起作用。终结器线程永远不会运行。终结器对于释放除内存之外的非托管资源非常重要。与操作系统句柄和托管对象持有的任何非托管内存指针一样。位图有那些。

首先要做的是运行Taskmgr.exe,Processes选项卡。单击View + Select Columns并勾选Handles,USER对象和GDI对象。在程序运行时观察这些计数器。如果你看到一个没有约束的攀爬,那么你有一个问题可能导致GDI +生成一个OOM异常。 GDI对象是常见原因。

仔细检查您的代码,并检查您是否在不再使用的任何图像或位图上调用Dispose()。注意细微的,比如分配PictureBox的Image属性。如果它不是null,你必须处理旧的。这当然很痛苦,很容易错过一个。因此,使用一个简单的策略,计算您创建的位图数量,比如第一百个调用GC.Collect + GC.WaitForPendingFinalizers()来触发集合和终结器扫描。

答案 1 :(得分:0)

过去我总是使用ANTS Memory Profiler来解决这类问题。它不是免费的,但它对托管代码中的内存/引用泄漏非常有效。当应用程序处于稳定状态并查看更改时,您只需拍摄几个快照。

答案 2 :(得分:0)

您可以安全地在using变量周围添加img块,只需稍加重构,您就可以对要声明的其他图像变量执行相同操作。

这应该至少使代码更具可读性,并减少忘记将一个代码添加到finally块的机会;我甚至可以为解决这个问题做出贡献。但是,您似乎手动处理了每个创建的图像对象。