【发布时间】:2012-03-20 13:56:10
【问题描述】:
编辑:添加代码(第 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,所以没有什么缺乏这方面的。 一直在任务管理器上看,我的内存使用量只有 1.72GB 到 1.78GB。
这是一个很难研究的问题,因为 OoM 似乎是一个不寻常的错误。 我想知道是否有人对处理这种异常有任何建议?我不是 Visual Studio 大师,有没有一种简单的方法可以监控资源/内存使用情况?
或者知道我可以使用哪些实用程序来提供帮助?
在这里转储错误信息,不确定代码 sn-ps 在这种情况下会有多大用处...
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 )
处理step of this guide,在“即时”窗口中使用 SOS 调试器,目的是查明异常是由托管代码还是非托管代码生成的。
上面的步骤表明这是托管代码的问题,因为显示了来自 SOS 的异常类型。
Exception object: 39594518
Exception type: System.OutOfMemoryException
Message: <none>
InnerException: <none>
StackTrace (generated):
The Heapdump I took 似乎不像我预期的那样是成千上万的位图。不是 100% 确定如何解释转储,所以看看我能找到什么。
现在不知道从这里搬到哪里! (搜索..)
编辑:
我一直在尝试将this blog 中的课程应用于我的问题。
从 PerfMon 开始
此图显示了我的程序从执行到捕获异常的过程。
前两个尖峰出现在触发解析扫描图像后,最后一个下降出现在捕获异常时。
问:比较所有堆中的 Virtual Bytes、Private Bytes 和 #Bytes 的曲线,它们是相互跟随还是发散? #Bytes 在所有不同的堆中的意义是什么? (就像我的公寓一样)
使用 !address -summary 检查内存 MEM_IMAGE 对应的 PrivateBytes(113MB) 非常准确。
问:大部分内存都去了哪里(哪个 RegionType)? RegionUsageFree 87.15% RegionUsageIsVAF 5.64% (Busy 43.89%) [通过 VirtualAlloc 分配的内存] RegionUsageImage 5.54% (Busy 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 是个问题吗?),因为我还没有发现任何明确的迹象。
答案:问题是在 3rd 方库中引起的。我无能为力。 经过深思熟虑和一些测试,其中包含仅涉及产生错误的方法的精简代码。
奖励给我认为我从中学到最多的东西。
【问题讨论】:
-
这是由 GDI+ 错误代码产生的异常,它与垃圾收集器无关,与 RAM 无关。当 GDI+ 在 unmanaged 虚拟内存中找不到足够大的孔以容纳位图时,它往往会崩溃。到目前为止,不处理位图是最典型的原因,而且位图太大了。您的问题中没有关于真正原因的提示。 64 位操作系统是百元的解决方案。
-
好的,我明白你在说什么,汉斯。我以前没有遇到过这种特殊情况。感谢您指出。
-
@HansPassant 感谢您的回复,我的印象是,如果我可以在即时窗口中
!printexception,那么必须从托管代码生成异常。所以我可能会浪费更多时间寻找错误的地方。 -
这一行 using (rotImg = new gmseBitmap(img.ExtractFrame(e))) 缺少一个 using,因为 ExtractFrame 正在分配一个新的位图,之前没有任何机会释放它下一次 GC 收集。另外,我会避免在 using 语句中使用全局变量,因为它不便于阅读(并且不能很好地处理异常)。尝试在 using 语句中声明在 using 语句中使用的变量,而不是在之前。
-
我的意思是两件事:1)一般来说是的,我建议你在 using 语句范围内声明变量,这将有助于有一个更清晰的代码来确保所有 Dispose (无需实际编写任何显式的 Dispose 调用),并在异常中幸存,以及 2)对于此特定行,您应该有两个 using 语句,而不是只有一个,因为 ExtractFrame 分配了一个位图。
标签: c# debugging out-of-memory sos