Is there any way to get more detail at runtime about an OutOfMemoryException? Or, can this exception somehow not be caught by the enclosing try/catch and instead a try/catch higher up the call stack? I cannot reproduce using WinDBG so it must be something I can log from the application.
I apologize for the long explanation, but there are a lot of possible causes to eliminate, which I explain.
I have read up on all the possibilities for an OutofMemoryException and basically eliminated all of them. Normally, application runs great, but occasionally on only certain computers, I am getting an OutOfMemoryException. As these reports are in the field on not reproducible locally, I only have logs to go by. But I have a fair amount of detail.
What is strange:
This has happened a couple times in different parts of the application recently. The first time, I concluded there was a corrupt .NET assembly, as the exception was occurring right when it would first load the System.Web.Serialization assembly. I could determine it was happening right during a method call where that assembly was used for the first time. Reimaging the computer (to be identical of original setup) and updating windows resolved this issue.
But, it seems highly unlikely to me that the second case, different client, happening within a few days, is also corruption. This one is happening in a location where no assemblies would be loaded. I'm rethinking the first error now. What I do know:
There is some use of COM objects. However, under normal conditions we run the app for weeks without memory problems, and when we get these exceptions, they are almost immediate, after only using around 20 relatively lightweight COM objects (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;
}
So, after all of that, I return to my opening question. Are there any known properties on the OutOfMemoryException object I can inspect, or calls I can make after the exception is thrown, to narrow this down?
And..is there any reason an OutOfMemoryException would not be caught by the first try/catch, but would be caught further up the call stack?
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.
What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.
History: The name C is derived from an earlier programming language called BCPL (Basic Combined Programming Language). BCPL had another language based on it called B: the first letter in BCPL.
Thank you all. The answer is somewhat curious and I had some details wrong which made it hard to figure out. The error is here:
Bitmap bmp;
using (var ms = new MemoryStream(data))
{
bmp = new Bitmap(ms);
}
bitmap = bmp;
In the remarks in documentation on the Bitmap constructor, I found this:
You must keep the stream open for the lifetime of the Bitmap.
Obviously, closing the MemoryStream immediately after constructing was violating this. A garbage collection between this and when I actually used the Bitmap was apparently creating the error. (EDIT: actually, it seems that a boundary exists around 1MiB where the FromStream function will decompress only so much of a JPEG file initially. For JPEG < 1MiB, the entire image is decompressed and it doesn't actually use the stream after the initialization. For larger JPEG, it will not read beyond the first 1MiB until those pixels are needed)
It's hard for me to imagine why Microsoft did it this way. I wouldn't want to keep the original stream open, either (which is an http connection) so only solution I see is to clone the bitmap:
// 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);
}
What led to my long frustrating search and the exclusion of a key piece of information was that the code executing on a client machine was a slightly older version, with only a subtle change. Graphics.FromImage() was being called on the bitmap in the previous version, but that had been removed. Still, that version functioned very well the vast majority of the time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With