有没有办法在运行时获取有关OutOfMemoryException的更多详细信息?或者,这个异常是否可以不被封闭的try / catch捕获,而是在调用堆栈中更高的try / catch?我无法使用WinDBG重现,所以它必须是我可以从应用程序中记录的东西。
我为长篇解释道歉,但有很多可能的原因要消除,我解释一下。
我已经阅读了OutofMemoryException的所有可能性,并基本上消除了所有这些可能性。通常情况下,应用程序运行良好,但偶尔只在某些计算机上,我得到一个OutOfMemoryException。由于这些报告在本地不可再现,我只有原木可供使用。但我有相当多的细节。
奇怪的是:
最近在应用程序的不同部分发生过几次这种情况。第一次,我得出结论,有一个损坏的.NET程序集,因为它首先加载System.Web.Serialization程序集时发生异常。我可以确定它是在第一次使用该程序集的方法调用期间发生的。重新映像计算机(与原始设置完全相同)和更新窗口解决了这个问题。
但是,对我来说,似乎不太可能在几天内发生的第二个案例,不同的客户也是腐败。这个发生在没有装载程序集的位置。我现在正在重新思考第一个错误。我所知道的:
有一些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不会被第一个try / catch捕获,但会被调用堆栈进一步捕获?
答案 0 :(得分:4)
谢谢大家。答案有点好奇,我有一些细节错误,这让人难以理解。错误在这里:
Bitmap bmp;
using (var ms = new MemoryStream(data))
{
bmp = new Bitmap(ms);
}
bitmap = bmp;
在Bitmap构造函数的文档备注中,我发现了这个:
您必须在Bitmap的生命周期内保持流打开。
显然,在构建之后立即关闭MemoryStream违反了这一点。在我和实际使用Bitmap之间的垃圾收集显然是在创建错误。 (编辑:实际上,似乎在1MiB周围存在一个边界,其中FromStream函数最初只会解压缩JPEG文件的大部分。对于JPEG< 1MiB,整个图像被解压缩并且它实际上并没有使用该流在初始化之后。对于较大的JPEG,在需要这些像素之前,它将不会超过第一个1MiB)
我很难想象微软为何会这样做。我也不想打开原始流(这是一个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(),但是已经删除了。尽管如此,这个版本在绝大多数时间里运作良好。