문제

We have a .net application in C#, which let's the users in a domain map to the users/roles in another domain.

In the property mapping panel (which is just a simple form shown as a dialog on the main form), there are two of listviews - 1 for the source domain, and the other for all the previously selected target domains. The listview is populated with users and group - one row for each user/group. We have an icon on each row to distinguish between a user and a group.

Everything was fine, until recently one of our clients came across the situation where the no. of GDI handles created by our application crossed 10k - which is the upper limit of possible number of handles that can be created by a process on a windows system. So, now we are thinking of the ways that we can use to reduce the number of handles created. Here are a few questions that we just want to be assured of. Which of these create a new handle (we suspect all of them) :

1)

this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(177)));

2)

this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));

3)

this.label2.Text = resources.GetString("label2.Text");

Please, do not care about the formatting of the question, and let me know, if any further information is required for a relevant conclusion.

What can we do to reduce the no. of handles created ? What are the proper ways to avoid this kind of scenario ? Can the handles be killed explicitly to reduce the count (just saying) ? Any suggestions, or things to be taken care of are appreciated. Thanks.

Also, we've this line of code:

this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));

So, how do we free up DC and Bitmaps handles ?

Screenshot from GDIView

도움이 되었습니까?

해결책 2

The main reason was that the form was initialized a lot many times than we had expected, and the ImageList was generated in the form's InitializeComponent() method. We just made that static, and shared that same ImageList object in all the initialized instances of that form. This controlled the number of GDI instances being created. We didn't had to call DeleteObject() on images either.

Initially, we tried this but, THIS WAS NOT THE REAL SOLUTION, which we figured out later

The modification shown below seems to put check on both, Bitmap handles as well as DC(DeviceContext) handles, and solves the problem in my scenario. I made a call to the WinAPI function DeleteObject() for each one of the image in the ImageList instance inside the form's Dispose() method, which after modification now looks like as shown below :

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            listViewMapping.Clear();
            listBoxPropertiesA.Items.Clear();
            listBoxPropertiesB.Items.Clear();

            if (imageList1 != null)
            {
                foreach (var img in imageList1.Images)
                {
                    DeleteObject(((System.Drawing.Bitmap)img).GetHbitmap());
                }
                imageList1.Dispose();
                imageList1 = null;
            }

            if (components != null)
            {
                components.Dispose();
                //DeleteObject();
            }
        }
        base.Dispose(disposing);
    }

다른 팁

Yes, your program is leaking System.Drawing objects. Every one count shown for "Bitmap" is an Image or Bitmap object in your program, every one count for "DC" (Device Context) is a Graphics object in your program.

These classes implement IDisposable, you are supposed to call their Dispose() method when you no longer use them. Forgetting to do so is a very common oversight. Since the DC count is almost the same as the Bitmap count, you've got a pretty good hint on where to start looking for the missing Dispose() calls or using statements, that Bitmap and Graphics object ought to be pretty close together.

There's furthermore a strong hint that the finalizer thread in your program isn't running often enough to keep you out of trouble. The simple explanation for this is that you just don't allocate enough objects to trigger a garbage collection. This is not entirely unusual, the Graphics and Bitmap classes are very small classes that by themselves are never enough to trigger a GC. So if your program is paint-heavy but not data heavy, or the data is stored by unmanaged code, like the classes in System.DirectoryServices, then the GC doesn't kick in often enough.

A dooms-day scenario is that the finalizer thread is broken and is deadlocked. Very hard to notice this without an unmanaged debugger, other than you noticing this exact kind of resource leakage. And an easy to overlook quirk, it takes two seconds for your program to stop running when you close the main window. Deadlock in the finalizer thread is typically caused by a threading problem, particularly with COM objects. Like the kind that are used in System.DirectoryServices. You need to enable unmanaged debugging and enable the Microsoft Symbol server to see it deadlocked in the stack trace.

Hopefully that's not it. Have a look-see at what the GC is doing by running Perfmon.exe. Right-click the graph and add the "# Gen 0 Collections" from the .NET CLR Memory" category for your program. With the expectation that it is not increasing at a steady clip.

If you cannot find the bug or it is located in code that you cannot fix then you may have to help and force the garbage collector to run. You could use, say, a Timer and call GC.Collect() in the Tick event handler. Or preferrably any other place in your code where you can reasonably put a counter that's representative for the number of leaked Graphics/Bitmap objects.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top