Do Bitmaps leak memory?

One of the fun parts of my job is talking to customers. However, this means that I sometimes I get asked tough questions…some of them issues in the .NET Compact Framework and sometimes we just need more information for our customers. So here is some information that I gleaned recently from talking to one of the .NET CF MVPs and David Wrighton – who is a developer on my team.

So the question is “Do bitmaps leak memory?” No, but without careful coding it could appear that they do.

The Issue

while(true)

{

    Bitmap b = new Bitmap(stream);

    // Use the bitmap

    // continue

}

In this small piece of code, you would expect that you would never get an OutOfMemoryException (OOM) because the “application” isn’t keeping any long-lived references to the Bitmap objects it is creating. However, it is allocating a lot of native memory as well as the managed memory for the Bitmaps. Because the Bitmaps aren’t disposed, when the application runs out of memory, the .NET CF CLR will invoke a garbage collection. However the Bitmap objects need to be moved to the finalizer queue before they are freed. Therefore, it’s possible that no memory is freed immediately causing the next Bitmap allocation to fail.

[See background posts on the GC and Finalizers by Steven Pratschner and myself]

The Best Fix

while(true)

{

    Bitmap b = new Bitmap(stream);

    // Use the bitmap

    // Release bitmap

    b.Dispose();

    // continue

}

The best way to prevent the OOM is to dispose of the bitmap when done using the object. This will cause the underlying native memory to be freed immediately and will prevent the non-determinism of the finalizer.

The Alternate ‘Fix’

This ‘fix’ is really used to help understand the inner workings of the .NET CF CLR a little more. I strongly suggest using the Dispose pattern as shown above.

while(true)

{

    Bitmap b;

    try {

        b = new Bitmap(stream);

    } catch (OutOfMemoryException) {

        GC.WaitForPendingFinalizers();

        b = new Bitmap(stream);

    }

    // Use the bitmap

    // continue

}

In this example, I put a try/catch surrounding the Bitmap constructor. If any part of the allocation fails, the CLR will attempt to perform a GC to reclaim memory. But, as noted above, all the Bitmap objects are placed on the finalizer queue. So we need the application to wait for all finalizers to complete before trying to allocate the object again. Of course, this could have some serious latency issues depending on the number of objects on the finalizer queue and the work each finalizer is required to do to complete.

Deeper information about Bitmap constructors

Here is a little deeper information on the way Bitmaps may be allocated. There are two major paths for allocation which may affect where the memory is allocated, but in the end have the same issues as indicated above.

  1. Bitmap constructor that takes a stream as a parameter

    • This will construct a DIB

    • DIBs are allocated out of the application process virtual memory (VM) address space

  2. Bitmap constructor that takes a height/width as parameters

    • This will construct a DDB

    • DDBs are allocated by the driver, typically, in the gwes.exe or possibly in dedicated video RAM. This will actually use physical and virtual memory that is not in the process VM space.

Directly from David Wrighton:

In short, we have 2 different types of Bitmap in our runtime with varying performance and allocation characteristics. DDBs are generally faster to manipulate and draw to the screen than DIBs, but they are constructed in an external memory space that can cause allocation confusion and cause the performance of calls to LockBits or Save to be slower. If a DIB is desired and you wish to construct it based on width and height, we provide a function that constructs a Bitmap with a width, height, and pixelformat specified. This function will construct a DIB instead of a DDB.

Hopefully this clears some confusion around the memory allocations surrounding bitmaps and how to “prevent leaks”.

Scott.

This posting is provided "AS IS" with no warranties, and confers no rights.