Welcome to MSDN Blogs Sign in | Join | Help

Unmanaged objects and memory usage

I few years ago I wrote a small utility class named Win32Window, so that I could write an automated tool to close down some of the dialogs that would appear whenever outlook lost a server connection (which was often, since it happened whenever I opened or closed my laptop). It wraps some of the high-level Win32 window handling functions. As part of that, I wrote a couple of routines to do screen and window capture, though I've never really used them.

Yesterday I got an email from someone who was using the routine for real, and it was eating his system's lunch (not actually the term he used, but words to those effects). It's always unfortunate when a poorly-coded sample makes it out into the wild. One would hope not to have any of those, but I learned in writing my book that a) It's not possible to be perfect on technical details, b) that if you cover enough details, you will make some mistakes, and c) some of them will be howlers, and make people think you are a total moron.

I was hoping this case wouldn't be that bad.

I opened up my project, modified my test app so that it would capture screenshows on a timer, started up perfmon to look at the .NET memory counters, and set it running, with one screenshot ever 100 mS. Though the counters on perfmon barely budged, in about 15 seconds things were getting slow, and soon after that, things got *really* slow - so slow, that I had to reboot.

In looking at my code, I found that I hadn't released some of the unmanaged graphics items, but fixing that issue didn't change the behavior at all. I switched to watching the normal (ie not .net) memory counters, and found that they went up a big chunk every time I did a screenshot.

I played around with the excelled CLR Profiler, but the current problem with the CLR profiler is that it doesn't have a very good delta view, and it's optimized for looking only at managed objects, not managed wrappers, which tend to be small in the managed world but big in the unmanaged world (think of a bitmap. On the managed size, there is a single IntPtr reference to the unmanaged object, which might easily exceed a megabyte in size). So, I didn't get anywhere with it.

I next added a "CLR" button in my app, to call:

GC.Collect();
GC.WaitForPendingFinalizers();

If my hunch was correct, this would find that managed object and finalize it, and the memory use would go down. That proved to be correct, which led me on a hunt further.

The final issue was actually two issues. First, I wasn't freeing a Graphics object that I should have. Second, I had code that was something like:

desktopWindow.Image = Win32Window.DesktopAsBitmap;

which works fine, but has the side effect of never disposing the current bitmap.

But I thought managed code meant I didn't have to think about such things...

Unfortunately, this is a case where our current solution doesn't work very well. In an ideal world, the GC would have cleaned up after those huge bitmaps, but unfortunately, it has no way of knowing how expensive the underlying unmanaged objects really are, so I could allocate tons of new Images with creating enough memory pressure to force a GC to occur. That's somewhat unfortunate.

The fix for the second issue is to call dispose on the old image. That gets me back to a steady-steate situation. The code will make its way up to the GDN archive in the next few days; if you're using this code, drop me a line and I'll send you an updated version.

Published Monday, December 08, 2003 4:33 PM by ericgu
Filed under: ,

Comments

Tuesday, December 09, 2003 8:06 PM by Kevin Westhead

# RE: Unmanaged objects and memory usage

I think this is a common problem when interacting with GDI objects in particular via managed code. For example, ImageList.ImageCollection.getItem(Int32) returns a new Bitmap object that the caller should dispose of when finished with, however this isn't necessarily clear from the documentation. Also, while on the subject of image lists, ImageList.Draw(Graphics, Int32, Int32, Int32) and ImageList.Draw(Graphics, Point, Int32) seem like wrappers around ImageList_DrawEx. ImageList.Draw(Graphics, Int32, Int32, Int32, Int32, Int32) however can potentially create a new Bitmap object that never gets disposed of, and therefore you can unknowingly increase your GDI object count.
Wednesday, December 17, 2003 9:02 AM by Steve Bolton

# RE: Unmanaged objects and memory usage

I had a similar problem with a slideshow viewer I wrote in C#. I used a couple of bitmap objects to show the current image and the image to be displayed next, and if I ran the show fast the old bitmap objects weren't being collected so I had to explicitly call dispose - very nasty!
Saturday, December 27, 2003 11:47 AM by Alienated VB developer

# RE: Unmanaged objects and memory usage

For a "new platform", it sure appears .NyET has inherited nearly all of Java's fundamental fatal flaws in regards to resource management! I'll stick to real languages that support real object destructors, fuck you all very much.
Thursday, April 15, 2004 11:04 AM by JiMMy

# re: Unmanaged objects and memory usage

I thought C# objects have destructor?
Sunday, August 01, 2004 5:45 PM by imagelist hater

# re: Unmanaged objects and memory usage

What is the deal with imagelists? They generally suck.

if I say imageList.Images[1] = new Bitmap(10,10), what happens to the old bitmap that was in slot 1? does it get disposed? or am I supposed to do that first?

Saturday, December 18, 2004 10:51 AM by MBA

# MBA

Helpful For MBA Fans.
New Comments to this post are disabled
 
Page view tracker