2009-05-18 7 views
3

J'étudie une fuite de ressources GDI dans une grande application. Afin de mieux comprendre comment ces problèmes se produisent, j'ai créé une très petite application que j'ai délibérément fait «couler». Voici un contrôle utilisateur simple qui devrait aboutir à la création de 100 objets Pen:Comment puis-je créer des fuites GDI dans Windows Forms!

 
public partial class TestControl : UserControl 
{ 
    private List pens = new List(); 

    public TestControl() 
    { 
     InitializeComponent(); 

     for (int i = 0; i < 100; i++) 
     { 
      pens.Add(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2)))); 
     } 

     this.Paint += new PaintEventHandler(TestControl_Paint); 
    } 

    void TestControl_Paint(object sender, PaintEventArgs e) 
    { 
     for (int i = 0; i < 100; i++) 
     { 
      e.Graphics.DrawLine(pens[i], 0, i, Width, i); 
     } 
    } 
} 

Cependant, lorsque je crée une instance de mon objet et l'ajouter à une forme, regardant ma demande avec TaskManager Je vois actuellement ~ 37 objets GDI. Si je ajoute à plusieurs reprises de nouveaux contrôles utilisateur TestObject à mon formulaire, je toujours seulement voir ~ 37 objets GDI.

Que se passe-t-il ici! Je pensais que le constructeur de System.Drawing.Pen utiliserait l'API GDI + pour créer un nouveau Pen, utilisant ainsi un nouvel objet GDI.

Je dois être fou ici. Si je ne peux pas écrire une application de test simple qui crée des objets GDI, comment puis-je en créer un qui les fuit!

Toute aide serait grandement appréciée.

Cordialement, Colin E.

+0

Un problème étrange là-bas, je suis d'accord. Avez-vous vérifié si le non. des objets GDI diminue de 37 si vous boucle * moins * que 37 fois? – Noldorin

+0

Oui - les 37 objets GDI semblent concerner le surdébit de l'application de test simple elle-même. Cela n'a rien à voir avec le nombre de boucles dans le code ci-dessus. Je pense que l'OregonGhost (ci-dessous) est sur quelque chose que GDI + n'utilise pas les poignées de GDI, que j'ai supposé qu'il a fait! J'aurais aimé qu'il y ait de la documentation là-bas pour vérifier cela. –

Répondre

2

Est-ce que le GDI + utilisez les poignées GDI? Je ne suis pas sûr, bien que j'ai lu quelque part qu'il existe une implémentation .NET System.Drawing qui repose sur GDI nu. Toutefois, vous pouvez peut-être essayer de trouver vos fuites avec un profileur comme AQTime à la place. Comment êtes-vous sûr que votre grande application fuit des poignées de GDI?

Comment êtes-vous sûr que votre grande application fuit des poignées de GDI? Le nombre dans le Gestionnaire des tâches est-il important? Si oui, utilisez-vous toujours GDI + ou GDI? Votre compte GDI de l'application de test augmente-t-il si vous créez votre contrôle plusieurs fois?

+0

Merci pour la réponse. Oui, c'était mon soupçon que GDI + n'utilise pas toujours les handles GDI. Je suis sûr que la grande application fuit les poignées GDI. Il utilise un grand nombre (> 500) et cela augmente avec le temps. Pour savoir si nous utilisons GDI + tout le temps, nous utilisons seulement System.Drawing qui, je présume, n'utilise que GDI + - est-ce correct? Merci. –

+1

... si System.Drawing.Pen n'utilise pas un descripteur GDI, quelle ressource puis-je libérer en le jetant? Désolé si ma seule question est devenue trois! –

+0

Je ne suis pas sûr. J'ai lu quelque part, comme mentionné, qu'il existe une implémentation System.Drawing utilisant GDI, alors que l'on s'attendrait généralement à ce que System.Drawing utilise GDI +. Peut-être aurait-il été sur Windows CE ou quelque chose. Je ne suis pas sûr que la création de poignées de fenêtre (formulaires et contrôles) puisse augmenter le nombre de handles GDI. Dans de nombreux cas, quand je fuyais une sorte de poignées, c'était à cause de contrôles fuyants plutôt que de choses GDI +. – OregonGhost

2

Vous ne perdez pas vraiment de ressources dans votre échantillon. Retirer le code de votre événement Load:

for (int i = 0; i < 100; i++) 
    { 
     pens.Add(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2)))); 
    } 

Votre gestionnaire d'événements de peinture devrait ressembler à ceci:

void TestControl_Paint(object sender, PaintEventArgs e) 
{ 
    for (int i = 0; i < 100; i++) 
    { 
     e.Graphics.DrawLine(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2))), 0, i, Width, i); 
    } 
} 

Maintenant vous serez coulants dans chaque appel de peinture. Commencer à minimiser/restaurer votre formulaire et voir les objets GDI sky rocket ...

Espérons que cela aide.

+1

Bonjour Denis ... bien essayé, mais avez-vous réellement testé cela? J'ai essayé et mon application n'utilisait qu'une poignée d'objets GDI. Cela n'a pas de sens! –

0

Je pense que le blog suivant peut avoir répondu à cette question:

Using GDI Objects the Right Way

Les objets GDI qui ne sont pas explicitement éliminés doivent être éliminés implicitement par leur finalise. (Bob Powell a également mentionné ce problème dans GDI+ FAQ)

Mais je doute que le garbage collector CLR peut supprimer les ressources GDI si rapidement que nous ne pouvons même pas voir les changements d'utilisation de la mémoire de TaskManager. Peut-être que l'implémentation GDI + actuelle n'utilise pas GDI.

J'ai essayé le morceau de code suivant pour générer plus d'objets GDI. Mais je ne pouvais toujours pas voir les changements du nombre de poignées GDI.

void Form1_Paint(object sender, PaintEventArgs e) 
{ 
    Random r = new Random(); 
    while (true) 
    { 
     for (int i = 0; i < 100; i++) 
     { 
      e.Graphics.DrawLine(
      new Pen(new SolidBrush(Color.FromArgb(r.Next()))), 0, i, Width, i); 
     } 
    } 
} 
1

Si vous voulez fuir un objet GDI de .NET, créez simplement un objet GDI et non le libérer:

[DllImport("gdi32.dll", EntryPoint="CreatePen", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)] 
private static extern IntPtr CreatePen(int fnStyle, int nWidth, int crColor); 

CreatePen(0, 0, 0); //(PS_SOLID, 0=1px wide, 0=black) 

Blingo Blango, vous êtes une fuite des stylos GDI.

je ne sais pas pourquoi vous voulez créer des fuites GDI. Mais votre question a demandé comment créer des fuites GDI à partir d'un WinForm - alors voilà.

+0

Vous devez également mentionner la RAISON qui fonctionne en code natif (P/Invoke) mais pas l'implémentation GDI/GDI + gérée. Je crois qu'il a quelque chose à voir avec .NET garbage collecte des ressources GDI/GDI +. – IDWMaster

+0

@IDWMaster: Le garbage collector .NET n'a pas la capacité de "collecter" la ressource native (par exemple les handles GDI). Tout code .net qui fait P/Invoke pour allouer un handle GDI est également responsable de faire un P/Invoke pour les libérer. Heureusement, tous les objets du framework .NET prennent soin de le faire. Le garbage collector n'a rien à voir avec ça. –

+0

Le GC peut cependant appeler un destructeur sur l'objet, ce qui pourrait; à son tour libérer la ressource non managée. – IDWMaster

0

Je pense que le compilateur n'utilise qu'une seule poignée.

Si je en delphi créer beaucoup de polices je prends juste la mémoire
mais si j'utilise le WinAPI CreateFont() je prends des objets GDI.

0

Créez deux boutons sur un formulaire. À l'intérieur de chaque bouton, ajoutez le code suivant. Dans un bouton, commentez la méthode Dispose.

Form _test = null; 
    for (int i = 0; i < 20; i++) 
    { 
     _test = new Form(); 
     _test.Visible = false; 
     _test.Show(); 
     _test.Hide(); 
     _test.Dispose(); 
    } 

Le bouton avec la fonction Dispose commentée vous indique la fuite. L'autre montre que Dispose fait que les poignées User et GDI restent les mêmes.

This est probablement la meilleure page que j'ai trouvé qui l'explique.