5

I was debugging resource leaks in my application and created a test app to test GDI object leaks. In OnPaint I create new icons and new bitmaps without disposing them. After that I check the increase of GDi objects in task manager for each of the cases. However, if I keep repainting the main window of my app, the number of GDI objects increases for icons, but there is no change for bitmaps. Is there any particular reason why icons are not getting cleaned up same way as bitmaps?

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        // 1. icon increases number of GDI objects used by this app during repaint.
        //var icon = Resources.TestIcon;
        //e.Graphics.DrawIcon(icon, 0, 0);

        // 2. bitmap doesn't seem to have any impact (only 1 GDI object)
        //var image = Resources.TestImage;
        //e.Graphics.DrawImage(image, 0, 0);
    }
}

Test Result:

  1. No icons and bitmaps - 30 GDI objects
  2. With bitmaps - 31 GDI object, the number doesn't change.
  3. With icons - 31 and then the number increases if you repaint the window.
username
  • 3,378
  • 5
  • 44
  • 75
  • Posible explcation is that it's garbage collection effect: `Bitmap` is usually *large* while `Icon` is *small* so large `Bitmap`s trigger up Garbage Collection that clears out `Bitmap`s (and *dispose* them) but not `Icon`s – Dmitry Bychenko Feb 13 '15 at 13:14
  • @DmitryBychenko Shouldn't newly created small objects belong to generation 0 and cleaned up more frequently? – username Feb 13 '15 at 13:27
  • yes, generation 0 is prone to be collected; another issue is that GC pays attention to CPU cache sizes etc. so a large object can trigger garbage collection. – Dmitry Bychenko Feb 13 '15 at 13:33
  • @DmitryBychenko I guess there is no easy way to check this experimentally. By the way, I'm using small images, they shouldn't differ much in size. – username Feb 13 '15 at 13:41
  • Those numbers are too small. You would be leaking if the numbers kept going up and up without ever decreasing on every paint call. – LarsTech Feb 13 '15 at 14:54
  • 1
    Since you are apparently using a static resource, have you tried keeping a bitmap copy and using DrawImage instead? `icon.ToBitmap()`. That way you would only have to refer to the icon once in your code. – Raheel Khan Mar 16 '15 at 02:34
  • Take a look in the .net framework source code. Search by DrawIcon. - http://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Graphics.cs,9aa931b146439086 – João Luiz Grigoletti Mar 24 '15 at 17:26
  • By What could understand in the brief look I gave the code is that when the instance of Graphic has a background , the icon is converted to bitmap before being rendered to avoid Alpha problems and keep your rendering in GDI + , avoiding the use of GDI. Now , do not know if that fund includes the background form – João Luiz Grigoletti Mar 24 '15 at 17:43

1 Answers1

1

I believe that you have to take care of the icons manually. I did some searching and found that GC takes care of the bitmaps but not the icons. The forms sometimes keep their own copy of the icons (i'm not sure why). A way to dispose icons can be found here: http://dotnetfacts.blogspot.com/2008/03/things-you-must-dispose.html

[DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);

private void GetHicon_Example(PaintEventArgs e)
{
// Create a Bitmap object from an image file.
Bitmap myBitmap = new Bitmap(@"c:\FakePhoto.jpg");

// Draw myBitmap to the screen.
e.Graphics.DrawImage(myBitmap, 0, 0);

// Get an Hicon for myBitmap.
IntPtr Hicon = myBitmap.GetHicon();

// Create a new icon from the handle.
Icon newIcon = Icon.FromHandle(Hicon);

// Set the form Icon attribute to the new icon.
this.Icon = newIcon;

// Destroy the Icon, since the form creates
// its own copy of the icon.
DestroyIcon(newIcon.Handle);
}
0xSingularity
  • 577
  • 6
  • 36