Возможная ошибка с .net winforms, или я просто что-то упустил? Утечка GDI-объекта - PullRequest
1 голос
/ 01 декабря 2011

Может кто-нибудь сказать мне, что идет не так?

После удаления тонны кода, чтобы обнаружить утечку нашего объекта GDI (с помощью диспетчера задач и наблюдая, как столбец «Объекты GDI» увеличивается до 10000 и происходит сбой нашего приложения)Я сократил код до только кода .net без какого-либо специального бизнес-кода.Мы все еще получаем проблему.

Я создал тестовое приложение для репликации проблемы, которое имеет следующее основное поведение:

  • Откройте форму 150 раз (150 - ничего особенного, просто число, достаточно большое, чтобылегко увидеть "застрявшие" ручки).Таймер в форме закроет форму через 1 секунду
  • Запустить сборщик мусора (не очень необходимо, но может помочь избавиться от «хороших» или «работающих» объектов, которые не являются частью проблемы)
  • Соблюдайте вручную количество объектов GDI приложения (вы должны сделать это до и после того, как вы откроете форму 150 раз.) Перед тем, как запустить тест, я обычно получаю счет 36, после того, как тест составляет около190. Каждый раз, когда я запускаю тест, это число увеличивается примерно на 150.

Теперь форма, которая запускается 150 раз, настраивается особым образом (давайте вызовем форму «BadForm».Статический объект данных связан с комбинированным списком в форме.

На BadForm есть поле со списком и таймер. Вот код для формы:

using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
namespace GDIObjectLeakTest
{
  public partial class MyForm :Form
  {
    public static DataTable CachedNodeType = new DataTable();

    public MyForm()
    {
      InitializeComponent();
      this.comboBox1.SelectedIndexChanged += new EventHandler(this.comboBox1_SelectedIndexChanged);
      this.Font = new Font("Modern No. 20", 8.249999F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0))); ;
      comboBox1.DataSource = CachedNodeType;
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
      Close();
    }

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    { }
  }
}   

Воткод для основной формы приложения, которое запускает тест. На нем 2 кнопки. Button1 запускает BadForm 150 раз. Кнопка 2 запускает сборщик мусора 100 раз (я полагаю, один или два раза мне не достаточно) (я используюсборщик мусора, чтобы доказать, что есть / не проблема).

private void button1_Click(object sender, EventArgs e)
{
  try
  {
    for(int i = 0; i < 150; i++)
    {
      //new SearchForm().Show();
      new MyForm().Show();
    }
  } catch(Exception ee)
  {
    throw;
  }
}

private void button2_Click(object sender, EventArgs e)
{
  for(int i = 0; i < 100; i++)
  {
    GC.Collect();
    GC.WaitForPendingFinalizers();
  }
}

Ответы [ 3 ]

2 голосов
/ 02 октября 2014

Попробуйте добавить это в начало вашего метода dispose (в файле конструктора):

comboBox1.DataSource = null;
0 голосов
/ 24 февраля 2013

Вы используете объект Font, который является объектом GDI и не удаляется до тех пор, пока вы не утилизируете его. Либо используйте объект Font с помощью оператора using, либо вызовите Font.Dispose для события FormClose.

0 голосов
/ 07 декабря 2011

Здесь я вижу две потенциальные проблемы:

  1. Если вы создаете экземпляр объекта, который реализует IDisposable, вы должны вызвать для него метод Dispose или обернуть его использование вusing блок. Форма реализует IDisposable , поэтому ваш код должен выглядеть следующим образом:

        using (Form myform = new Form())
        {
            myform.Show();
        }   //frees resource by calling Dispose automatically
    

    в противном случае вы увидите утечки памяти, которые видите здесь, потому что вы создаете новые экземпляры своей формы,но тогда вы никогда не освободите его ресурсы.Сборка мусора может в конечном итоге освободить ресурсы Windows для вас в WinForms из-за того, как финализаторы записываются в BCL, но вызов Dispose сделает это немедленно.

  2. Вы создаете новыйFont объект и присвоение его свойству Font при каждой инициализации вашей формы.Не то чтобы это обязательно плохо (сгенерированный дизайнером код делает это обильно), но каждый новый экземпляр Font занимает дескриптор GDI, который не будет освобожден автоматически, если вы не вызовете Font.Dispose.Я предполагаю, что вы оставляете за собой другой объект Font, который может не удаляться должным образом.Возможно, вы захотите оптимизировать это каким-либо образом (например, путем совместного использования экземпляра Font), если вы создаете огромное число этих объектов. Как минимум в одном случае , не вызывая Dispose для шрифтов, приведет к утечке памяти.

...