有什么方法可以在运行时获得有关OutOfMemoryException的更多详细信息吗?或者,封闭的try/catch是否能够以某种方式捕获此异常,而不是调用堆栈中较高的try/catch?我不能用windbg复制,所以它必须是我可以从应用程序中记录的东西。
我为冗长的解释道歉,但有很多可能的原因要消除,我解释。
我已经阅读了OutOfMemoryException的所有可能性,并基本上消除了所有这些可能性。通常,应用程序运行得很好,但偶尔在某些计算机上,我会遇到OutOfMemoryException。由于这些报告在现场不可本地复制,我只有日志可供参考。但我有很多细节。
奇怪的是:
逻辑上可能在附近分配内存的任何东西都在try/catch中,但异常被视为未处理(并在调用堆栈的更高位置捕获)
没有使用StringBuffers
即使在重新启动并重新启动应用程序之后,也会发生异常。
异常仅在几分钟后发生,并且只分配了大约30兆字节的内存,块大小不超过1.5兆字节。
已验证应用程序(为“任意”处理器构建)是否以64位运行。
磁盘空间(270GB可用空间)和页面文件都已启用。
似乎不是一个可能的loh片段化问题。
最近在应用程序的不同部分发生过几次这种情况。第一次,我断定存在损坏的.NET程序集,因为异常是在它第一次加载System.Web.Serialization程序集时发生的。我可以确定它是在第一次使用该程序集的方法调用期间发生的。重新映像计算机(使其与原始安装程序相同)和更新Windows解决了此问题。
但是,在我看来,第二个案件,不同的客户,在几天内发生,也不太可能是腐败。此事件发生在不会加载程序集的位置。我正在重新考虑第一个错误。我知道的是:
它发生在线程池线程中(system.timer s.timer,[statthread])
少量线程处于活动状态(它发生在下载1MIB文件的时候。它被读入一个内存流,所以它可能会有2兆字节那么大。然后将其馈送到system.drawing.bitmap构造函数,生成大约8mib的位图。但是,这都在捕获System.Exception的try/catch中。try/catch中唯一没有返回的是byte[]引用,它应该只是一个引用副本,而不是任何内存分配。
在此之前,没有进行过其他重要的内存分配。在我的本地版本中查看堆,它应该以相同的方式运行,只显示应用程序图标和几十个小对象堆上的对象。
它可以在具有特定输入的特定系统上重复。然而,这些系统是从彼此克隆的。唯一明显的变化是windows更新的顺序。
我运行的程序集已签名。难道没有一个校验和可以确保它没有损坏吗?所有系统组件都一样?我不明白这个实例是如何被损坏的dll甚至数据解释的。
在发生异常时查看调用堆栈是毫无帮助的。我在下面的代码中指出抛出异常的位置。
有一些COM对象的用法。然而,在正常情况下,我们运行应用程序数周而没有内存问题,当我们遇到这些异常时,它们几乎是即时的,只使用了大约20个相对轻量级的com对象(iupnpdevice)
// Stack Trace indicates this method is throwing the OutOfMemoryException
// It isn't CAUGHT here, though, so not in the try/catch.
//
internal void Render(int w, int h)
{
if (bitmap != null)
{
bitmap.Dispose();
bitmap = null;
}
if (!String.IsNullOrEmpty(url))
{
// this information is printed successfully to log, with correct url
// exception occurs sometime AFTER this somehow.
Logger.Default.LogInfo("Loading {0}", url);
// when file contents changed (to go from 1MiB to 500MiB, the error went away)
byte[] data = DownloadBinaryFile(url);
if (data != null)
{
try
{
Bitmap bmp;
using (var ms = new MemoryStream(data))
{
bmp = new Bitmap(ms);
}
bitmap = bmp;
}
catch (Exception)
{
// We do not catch anything here.
Logger.Default.LogWarning("WARNING: Exception loading image {0}", url);
}
}
//
// if we had any errors, just skip this slide
//
if (bitmap == null)
{
return;
}
// QUESTION EDIT:
// in the problematic version, there was actually an unnecessary
// call here that turns out to be where the exception was raised:
using( Graphics g = Graphics.FromImage(bitmap)) {
}
}
}
// calling this would trigger loading of the System.Web assembly, except
// similar method has been called earlier that read everything. Only
// class being used first time is the BinaryReader, which is in System.IO
// and already loaded.
internal static byte[] DownloadBinaryFile(String strURL, int timeout = 30000)
{
try
{
HttpWebRequest myWebRequest = HttpWebRequest.Create(strURL) as HttpWebRequest;
myWebRequest.KeepAlive = true;
myWebRequest.Timeout = timeout;
myWebRequest.ReadWriteTimeout = timeout;
myWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)";
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
using (HttpWebResponse myWebResponse = myWebRequest.GetResponse() as HttpWebResponse)
{
if (myWebResponse.StatusCode != HttpStatusCode.OK)
{
Logger.Default.LogWarning("WARNING: Response {0} loading {1}", myWebResponse.StatusCode, strURL);
return null;
}
using (Stream receiveStream = myWebResponse.GetResponseStream())
{
using (BinaryReader readStream = new BinaryReader(receiveStream))
{
// this extension method uses MemoryStream, but seems irrelevant since we don't catch anything here.
return readStream.ReadAllBytes();
}
}
}
}
catch (Exception e)
{
// we do not catch anything here.
Logger.Default.LogError("ERROR: Exception {0} loading {1}", e.Message, strURL);
}
return null;
}
所以,在所有这些之后,我回到我的开场白。OutOfMemoryException对象上是否有任何已知的属性可以检查,或者在抛出异常后可以调用以缩小范围?
还有..OutOfMemoryException是否有任何原因不会被第一次尝试/捕获捕获捕获,而是会被进一步捕获到调用堆栈中?
最佳答案
谢谢大家。答案有点奇怪,我有一些错误的细节,很难弄清楚。错误就在这里:
Bitmap bmp;
using (var ms = new MemoryStream(data))
{
bmp = new Bitmap(ms);
}
bitmap = bmp;
在位图构造函数文档中的注释中,我发现:
在位图的生命周期内,必须保持流打开。
显然,在构造之后立即关闭内存流违反了这一点。从这到我实际使用位图之间的垃圾收集显然是在创建错误。(编辑:事实上,似乎一个边界存在于1MIB附近,FasFipe函数最初只会解压缩一个JPEG文件。对于jpeg我很难想象微软为什么这么做。我也不想让原始流保持打开状态(这是一个http连接),所以我看到的唯一解决方案是克隆位图:
// Create a Bitmap object from a file.
using (var ms = new MemoryStream(data))
{
bmp = new Bitmap(ms);
Rectangle cloneRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.PixelFormat format = bmp.PixelFormat;
this.bitmap = bmp.Clone(cloneRect, bmp.PixelFormat);
}
导致我长期沮丧的搜索和排除关键信息的原因是,在客户机上执行的代码是一个稍旧的版本,只有细微的更改。在以前的版本中,正在对位图调用graphics.fromImage(),但已将其删除。尽管如此,这个版本在绝大多数情况下都运行得非常好。
关于c# - System.Drawing.Bitmap中的C#OutOfMemoryException,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46803748/