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:
- Anything that might logically be allocating memory in the vicinity is in a try/catch, but the exception is treated as unhandled (and caught much higher up the call stack)
- There are no StringBuffers in use
- The exception happens even after rebooting and restarting the application.
- The exception occurs after only a couple minutes, and only about 30MiB of memory allocated, in chunks no more than 1.5MiB.
- Verified the application (built for "any" processor) is running as 64 bit.
- no shortage of disk space (270Gb free) and pagefile is enabled.
- does not appear to be a possible LoH fragmentation issue.
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:
- It's happening in a thread pool thread (System.Timers.Timer, [Statthread])
- Small number of threads active (< 5)
- It happens around the time a 1MiB file is downloaded. This is read into a MemoryStream, so that could be as big as 2MiB. That is then fed to a System.Drawing.Bitmap constructor, resulting in a Bitmap that would be about 8MiB. However, that is all in a try/catch that catches System.Exception. The only thing not in the try/catch is returning the byte[] reference, which should just be a reference copy, not any memory allocation.
- No other significant memory allocations have been done before this time. Reviewing heap in my local version, which should be running identically, shows just the app icon and a couple dozen objects that would be on Small Object Heap.
- it is repeatable on a specific system with specific input. However, these systems are cloned from one another. Only obvious variation would be the order of windows updates.
- The assembly I'm running is signed. Isn't there a checksum that ensures it isn't corrupted? Same for all the system assemblies? I don't see how this instance could be explained by corrupted dlls, or even data.
- Viewing the call stack at time of exception is surprisingly unhelpful. I indicate in the code below where exception is thrown.
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?