Out of Memory Exception

P

Pete Davis

I have an app that's dealing with a few hundred bitmap thumbnails.

I only instantiate the bitmaps that are visible at a given time and dispose
of them if they get scrolled out of view. I've used the .NET memory profiler
and I've verified there's no leak in my bitmaps, nor are there any other
apparent leaks. So, at a given time, there may be 30 or so bitmaps displayed
at a given time.

Still, somehow, I'm getting random errors in the painting that I can only
assume is coming from some sort of GDI resource issue. I've also had
situations where painting would happen outside of the control, where GDI was
clearly confused.

Here's the tail end of a stack dump from a recent OutOfMemory exception:

system.drawing.dll!System.Drawing.Graphics.FromHdcInternal(int hdc = 0) +
0x56 bytes
system.windows.forms.dll!System.Windows.Forms.GraphicsBufferManager.DibGraphicsBufferManager.CreateBuffer(int
src = 738299658, int offsetX = 0, int offsetY = 0, int width = 1333, int
height = 1086) + 0x1e4 bytes
system.windows.forms.dll!System.Windows.Forms.GraphicsBufferManager.DibGraphicsBufferManager.AllocBuffer(System.Drawing.Graphics
targetGraphics = <undefined value>, int targetDC = 738299658,
System.Drawing.Rectangle targetBounds = {X=0 Y=0 Width=1333 Height=1086}) +
0x1a4 bytes
system.windows.forms.dll!System.Windows.Forms.GraphicsBufferManager.DibGraphicsBufferManager.AllocBufferInTempManager(System.Drawing.Graphics
targetGraphics = <undefined value>, int targetDC = 738299658,
System.Drawing.Rectangle targetBounds = {X=0 Y=0 Width=1333 Height=1086}) +
0x3e bytes
system.windows.forms.dll!System.Windows.Forms.GraphicsBufferManager.DibGraphicsBufferManager.AllocBuffer(int
target = 738299658, System.Drawing.Rectangle targetBounds = {X=0 Y=0
Width=1333 Height=1086}) + 0x3f bytes
system.windows.forms.dll!System.Windows.Forms.Control.WmPaint(System.Windows.Forms.Message
m = {System.Windows.Forms.Message}) + 0x23a bytes
system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message
m = {System.Windows.Forms.Message}) + 0x2d6 bytes

The code that triggers this is actually an Application.DoEvents() call, so
it's simply the OnPaint being called, which is where the control paints the
thumbnails.

Any ideas on how I can diagnose this? I was sure .NET Memory Profiler would
show something leaking, but there's just nothing leaking that I can find..
The app is using about 70MB of ram, but it stays pretty consistently around
that figure through the lifetime of the app and is well within my 2.5GB of
physical ram.

I do make some P/Invoke GDI calls in the OnPaint. I have a method that draw
captions for each thumbnail and it has to call GetTextExtentPoint32 to
determine the text size. To do this, I basically do:

IntPtr hdc = g.GetHdc();
IntPtr oldFont = SelectObject(hdc, _labelFont.ToHfont());
SIZE textSize = new SIZE();
GetTextExtentPoint32(hdc, nameTxt, nameTxt.Length, ref textSize);
SelectObject(hdc, oldFont);
g.ReleaseHdc(hdc);

Am I doing something wrong here that's causing the HDC to leak?

Thanks for any ideas anyone can provide in tracking this down.
 
T

Truong Hong Thi

Hi Pete,
I only instantiate the bitmaps that are visible at a given >time and dispose
of them if they get scrolled out of view.
Still, somehow, I'm getting random errors in the painting >that I can only
assume is coming from some sort of GDI resource >issue.

I also had to write code that generating thumbnails some time ago. .NET
throws OutOfMemoryException if it does not recognize the image format.
This happen for example when loading the image file whose format is
unreconized/unsupported by .NET. Methods might raise this exception
include Image.FromFile, Bitmap constructor, etc, see their "remark"
section. You should debug your app to see if it is the cause.

Regards,
Thi

Thi
 
P

Pete Davis

Uou're right, and I realize this. In fact, I catch the exception if it
happens on the load and report an invalid file format..

In this case, as you can see from the stack trace, the OutOfMemoryException,
however, the exception is being thrown in
System.Drawing.Graphics.FromHDCInternal and is being triggered by a WM_PAINT
message, not an image load.

Pete
 
W

Willy Denoyette [MVP]

Question is how large are the bitmaps?

Willy.

Pete Davis said:
I have an app that's dealing with a few hundred bitmap thumbnails.

I only instantiate the bitmaps that are visible at a given time and
dispose of them if they get scrolled out of view. I've used the .NET
memory profiler and I've verified there's no leak in my bitmaps, nor are
there any other apparent leaks. So, at a given time, there may be 30 or so
bitmaps displayed at a given time.

Still, somehow, I'm getting random errors in the painting that I can only
assume is coming from some sort of GDI resource issue. I've also had
situations where painting would happen outside of the control, where GDI
was clearly confused.

Here's the tail end of a stack dump from a recent OutOfMemory exception:

system.drawing.dll!System.Drawing.Graphics.FromHdcInternal(int hdc = 0) +
0x56 bytes

system.windows.forms.dll!System.Windows.Forms.GraphicsBufferManager.DibGraphicsBufferManager.CreateBuffer(int
src = 738299658, int offsetX = 0, int offsetY = 0, int width = 1333, int
height = 1086) + 0x1e4 bytes

system.windows.forms.dll!System.Windows.Forms.GraphicsBufferManager.DibGraphicsBufferManager.AllocBuffer(System.Drawing.Graphics
targetGraphics = <undefined value>, int targetDC = 738299658,
System.Drawing.Rectangle targetBounds = {X=0 Y=0 Width=1333 Height=1086})
+ 0x1a4 bytes

system.windows.forms.dll!System.Windows.Forms.GraphicsBufferManager.DibGraphicsBufferManager.AllocBufferInTempManager(System.Drawing.Graphics
targetGraphics = <undefined value>, int targetDC = 738299658,
System.Drawing.Rectangle targetBounds = {X=0 Y=0 Width=1333 Height=1086})
+ 0x3e bytes

system.windows.forms.dll!System.Windows.Forms.GraphicsBufferManager.DibGraphicsBufferManager.AllocBuffer(int
target = 738299658, System.Drawing.Rectangle targetBounds = {X=0 Y=0
Width=1333 Height=1086}) + 0x3f bytes

system.windows.forms.dll!System.Windows.Forms.Control.WmPaint(System.Windows.Forms.Message
m = {System.Windows.Forms.Message}) + 0x23a bytes

system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message
m = {System.Windows.Forms.Message}) + 0x2d6 bytes

The code that triggers this is actually an Application.DoEvents() call, so
it's simply the OnPaint being called, which is where the control paints
the thumbnails.

Any ideas on how I can diagnose this? I was sure .NET Memory Profiler
would show something leaking, but there's just nothing leaking that I can
find.. The app is using about 70MB of ram, but it stays pretty
consistently around that figure through the lifetime of the app and is
well within my 2.5GB of physical ram.

I do make some P/Invoke GDI calls in the OnPaint. I have a method that
draw captions for each thumbnail and it has to call GetTextExtentPoint32
to determine the text size. To do this, I basically do:

IntPtr hdc = g.GetHdc();
IntPtr oldFont = SelectObject(hdc, _labelFont.ToHfont());
SIZE textSize = new SIZE();
GetTextExtentPoint32(hdc, nameTxt, nameTxt.Length, ref textSize);
SelectObject(hdc, oldFont);
g.ReleaseHdc(hdc);

Am I doing something wrong here that's causing the HDC to leak?

Thanks for any ideas anyone can provide in tracking this down.
 
P

Pete Davis

Willy Denoyette said:
Question is how large are the bitmaps?

Willy.

Willy, some of the bitmaps are quite large, but they are freed before the
thumbnails are drawn and all that remains loaded are whatever thumbnails are
visible at a given time. So, for example, I may have 30 or 40 100x100
bitmaps. The problem was happening regardless of whether the bitmaps were
created from the original image, or from my thumbnail cache (which keeps a
copy of the thumbnail after the first time it's created).

After some testing, I think I've narrowed it down to the code that draws the
caption text under each thumbnail. Though the problem isn't 100% reliably
reproduceable, I've done enough testing now that I would think I would have
run into the problem a few times by now, but haven't, since I removed the
code to draw the text.

Here is the full code that draws the text:

private void DrawFileText(Graphics g, Point thumbLocation, ThumbInfo ti)
{
IntPtr hdc = g.GetHdc();
IntPtr oldFont = SelectObject(hdc, _labelFont.ToHfont());

string nameTxt = Path.GetFileName(ti.Filename);
if (nameTxt.Length > 25)
{
nameTxt = nameTxt.Substring(0, 23) + "...";
}

SIZE textSize = new SIZE();
GetTextExtentPoint32(hdc, nameTxt, nameTxt.Length, ref textSize);

int xNameLoc = (thumbLocation.X + (_thumbSize.Width / 2)) - (textSize.cx
/ 2);
int yNameLoc = thumbLocation.Y + _thumbSize.Height + 3;

string dimTxt = ti.ImageSize.Width.ToString() + "x" +
ti.ImageSize.Height.ToString();
GetTextExtentPoint32(hdc, dimTxt, dimTxt.Length, ref textSize);

int xDimLoc = (thumbLocation.X + (_thumbSize.Width / 2)) - (textSize.cx
/ 2);
int yDimLoc = yNameLoc + textSize.cy;

SelectObject(hdc, oldFont);
g.ReleaseHdc(hdc);

g.DrawString(nameTxt, _labelFont, Brushes.Black, new PointF((float)
xNameLoc, (float) yNameLoc));
g.DrawString(dimTxt, _labelFont, Brushes.Black, new PointF((float)
xDimLoc, (float) yDimLoc));
}

There must be a leak in here somewhere that .NET Memory Profiler isn't
catching. If I comment out the call to this method, I simply can't seem to
reproduce the problem. I will continue testing to try to be certain.

Pete
 
P

Pete Davis

[snip]
Here is the full code that draws the text:

private void DrawFileText(Graphics g, Point thumbLocation, ThumbInfo ti)
{
IntPtr hdc = g.GetHdc();
IntPtr oldFont = SelectObject(hdc, _labelFont.ToHfont());

string nameTxt = Path.GetFileName(ti.Filename);
if (nameTxt.Length > 25)
{
nameTxt = nameTxt.Substring(0, 23) + "...";
}

SIZE textSize = new SIZE();
GetTextExtentPoint32(hdc, nameTxt, nameTxt.Length, ref textSize);

int xNameLoc = (thumbLocation.X + (_thumbSize.Width / 2)) -
(textSize.cx / 2);
int yNameLoc = thumbLocation.Y + _thumbSize.Height + 3;

string dimTxt = ti.ImageSize.Width.ToString() + "x" +
ti.ImageSize.Height.ToString();
GetTextExtentPoint32(hdc, dimTxt, dimTxt.Length, ref textSize);

int xDimLoc = (thumbLocation.X + (_thumbSize.Width / 2)) - (textSize.cx
/ 2);
int yDimLoc = yNameLoc + textSize.cy;

SelectObject(hdc, oldFont);
g.ReleaseHdc(hdc);

g.DrawString(nameTxt, _labelFont, Brushes.Black, new PointF((float)
xNameLoc, (float) yNameLoc));
g.DrawString(dimTxt, _labelFont, Brushes.Black, new PointF((float)
xDimLoc, (float) yDimLoc));
}

I have little doubt, at this point, that the above code is the problem. I
just can't figure out what's wrong with it. If anyone has any ideas, I'd
really appreciate it.

If the call to this method is commented out (it's called from OnPaint), then
everything works fine. If this method isn't commented out, eventually I get
an error in the paint, somewhere deep in GDI and usually not with this
particular OnPaint in the call stack. It appears that this method is somehow
corrupting GDI or perhaps leaking GDI resources or something.

The Dllmports for the two API functions used are:

[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
public static extern int GetTextExtentPoint32(IntPtr hdc, String str, int
len, ref SIZE size);

[DllImport("gdi32.dll")]
static public extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);


The SIZE struct is:

[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;
}


Can anyone spot a problem anywhere in there?

Thanks.

Pete
 
G

Guest

A while back, I had Out of Memory exceptions in one of my applications even
though the development PC had 2GB of memory (no memory leaks). I found this
during a Google search:

http://support.microsoft.com/kb/825680/

I applied the .NET Framework 1.1 Service Pack 1 and it fixed the issue.
This may or may not help you, but it is something to consider.
 
P

Pete Davis

I already have the service pack installed. It would have been nice if that
were the problem :)

Pete
 
W

Willy Denoyette [MVP]

Pete Davis said:
[snip]
Here is the full code that draws the text:

private void DrawFileText(Graphics g, Point thumbLocation, ThumbInfo ti)
{
IntPtr hdc = g.GetHdc();
IntPtr oldFont = SelectObject(hdc, _labelFont.ToHfont());

string nameTxt = Path.GetFileName(ti.Filename);
if (nameTxt.Length > 25)
{
nameTxt = nameTxt.Substring(0, 23) + "...";
}

SIZE textSize = new SIZE();
GetTextExtentPoint32(hdc, nameTxt, nameTxt.Length, ref textSize);

int xNameLoc = (thumbLocation.X + (_thumbSize.Width / 2)) -
(textSize.cx / 2);
int yNameLoc = thumbLocation.Y + _thumbSize.Height + 3;

string dimTxt = ti.ImageSize.Width.ToString() + "x" +
ti.ImageSize.Height.ToString();
GetTextExtentPoint32(hdc, dimTxt, dimTxt.Length, ref textSize);

int xDimLoc = (thumbLocation.X + (_thumbSize.Width / 2)) -
(textSize.cx / 2);
int yDimLoc = yNameLoc + textSize.cy;

SelectObject(hdc, oldFont);
g.ReleaseHdc(hdc);

g.DrawString(nameTxt, _labelFont, Brushes.Black, new PointF((float)
xNameLoc, (float) yNameLoc));
g.DrawString(dimTxt, _labelFont, Brushes.Black, new PointF((float)
xDimLoc, (float) yDimLoc));
}

I have little doubt, at this point, that the above code is the problem. I
just can't figure out what's wrong with it. If anyone has any ideas, I'd
really appreciate it.

If the call to this method is commented out (it's called from OnPaint),
then everything works fine. If this method isn't commented out, eventually
I get an error in the paint, somewhere deep in GDI and usually not with
this particular OnPaint in the call stack. It appears that this method is
somehow corrupting GDI or perhaps leaking GDI resources or something.

The Dllmports for the two API functions used are:

[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
public static extern int GetTextExtentPoint32(IntPtr hdc, String str, int
len, ref SIZE size);

[DllImport("gdi32.dll")]
static public extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);


The SIZE struct is:

[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;
}


Can anyone spot a problem anywhere in there?

Thanks.

Pete
My best guess is that somehow you are exhausting the GDI heap and handles
because you aren't deleting the Font object handle.

[DllImport("Gdi32.dll")]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool DeleteObject(

IntPtr hObject // handle to graphic object

);

DeleteObject(oldFont);



Willy.
 
P

Pete Davis

Willy,

I'll test that, but I'm curious:

The font is a member that's instantiated in the constructor and disposed in
the Dispose() method of the thumbnail control. Is ToHfont() creating a new
font handle every time I call it? The documentation doesn't say anything
about deleting the handle after calling ToHfont(). I just assumed each call
would be returning the same handle since it's the same font object.

I suppose I could just get it once in the constructor and that would take
care of the problem, if that is the problem. I'll post again after testing.
Thanks.

Pete

Willy Denoyette said:
Pete Davis said:
[snip]
Here is the full code that draws the text:

private void DrawFileText(Graphics g, Point thumbLocation, ThumbInfo ti)
{
IntPtr hdc = g.GetHdc();
IntPtr oldFont = SelectObject(hdc, _labelFont.ToHfont());

string nameTxt = Path.GetFileName(ti.Filename);
if (nameTxt.Length > 25)
{
nameTxt = nameTxt.Substring(0, 23) + "...";
}

SIZE textSize = new SIZE();
GetTextExtentPoint32(hdc, nameTxt, nameTxt.Length, ref textSize);

int xNameLoc = (thumbLocation.X + (_thumbSize.Width / 2)) -
(textSize.cx / 2);
int yNameLoc = thumbLocation.Y + _thumbSize.Height + 3;

string dimTxt = ti.ImageSize.Width.ToString() + "x" +
ti.ImageSize.Height.ToString();
GetTextExtentPoint32(hdc, dimTxt, dimTxt.Length, ref textSize);

int xDimLoc = (thumbLocation.X + (_thumbSize.Width / 2)) -
(textSize.cx / 2);
int yDimLoc = yNameLoc + textSize.cy;

SelectObject(hdc, oldFont);
g.ReleaseHdc(hdc);

g.DrawString(nameTxt, _labelFont, Brushes.Black, new PointF((float)
xNameLoc, (float) yNameLoc));
g.DrawString(dimTxt, _labelFont, Brushes.Black, new PointF((float)
xDimLoc, (float) yDimLoc));
}

I have little doubt, at this point, that the above code is the problem. I
just can't figure out what's wrong with it. If anyone has any ideas, I'd
really appreciate it.

If the call to this method is commented out (it's called from OnPaint),
then everything works fine. If this method isn't commented out,
eventually I get an error in the paint, somewhere deep in GDI and usually
not with this particular OnPaint in the call stack. It appears that this
method is somehow corrupting GDI or perhaps leaking GDI resources or
something.

The Dllmports for the two API functions used are:

[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
public static extern int GetTextExtentPoint32(IntPtr hdc, String str, int
len, ref SIZE size);

[DllImport("gdi32.dll")]
static public extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);


The SIZE struct is:

[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;
}


Can anyone spot a problem anywhere in there?

Thanks.

Pete
My best guess is that somehow you are exhausting the GDI heap and handles
because you aren't deleting the Font object handle.

[DllImport("Gdi32.dll")]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool DeleteObject(

IntPtr hObject // handle to graphic object

);

DeleteObject(oldFont);



Willy.
 
P

Pete Davis

This was in fact the problem and this seems to me to be a documentation
error as there's no mention of it in the documentation.

However, underneath, according to Reflector, CreateFontIndirect is being
called each time you call ToHfont(), and obviously, the font is never
deleted, so you're obligated to do a p/Invoke to delete it if you call
ToHfont().

Pete
 
W

Willy Denoyette [MVP]

Pete Davis said:
This was in fact the problem and this seems to me to be a documentation
error as there's no mention of it in the documentation.

However, underneath, according to Reflector, CreateFontIndirect is being
called each time you call ToHfont(), and obviously, the font is never
deleted, so you're obligated to do a p/Invoke to delete it if you call
ToHfont().

Pete

The call to ToHfont returns a new Windows GDI handle wrapped in an IntPtr,
so you need to call DeleteObject when done with it.
But, you are right, this isn't documented in v1.1, however, it looks like
this is corrected in the new docs's.
http://msdn2.microsoft.com/en-us/library/system.drawing.font.tohfont.aspx
Willy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top