编辑:添加了代码(第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
(目前正在阅读更多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
此图显示了我的程序从执行到捕获异常的位置。
在触发解析扫描图像后出现前两个尖峰,捕捉到异常时会发生最后一次下降。
问:比较所有堆中的虚拟字节,私有字节和#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奖励了我从中学到的最多。
答案 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块的机会;我甚至可以为解决这个问题做出贡献。但是,您似乎手动处理了每个创建的图像对象。