Важно ли располагать SolidBrush и Pen?
Недавно я встретил этот элемент управления VerticalLabel в CodeProject.
Я заметил, что метод OnPaint создает, но не удаляет объекты Pen и SolidBrush.
Это имеет значение, и если да, то как я могу продемонстрировать все проблемы, которые могут возникнуть?
EDIT
Это не вопрос о шаблоне IDisposable в целом. Я понимаю, что вызывающие абоненты обычно должны вызывать Dispose в любом классе, который реализует IDisposable.
Я хочу знать, какие проблемы (если таковые имеются) можно ожидать, когда объект GDI + не будет удален, как в приведенном выше примере. Понятно, что в связанном примере OnPaint может быть вызван много раз до того, как сборщик мусора начнет работать, поэтому есть возможность выбежать из ручек.
Однако я подозреваю, что GDI + в некоторых случаях повторно использует дескрипторы (например, если вы используете перо определенного цвета из класса Pens, оно кэшируется и повторно используется).
То, что я пытаюсь понять, может ли код, подобный этому в связанном примере, уйти от пренебрежения вызовом Dispose.
И если нет, чтобы увидеть образец, который продемонстрировал, какие проблемы он может вызвать.
Я должен добавить, что я очень часто (включая документацию OnPaint в MSDN) видел образцы кода WinForms, которые не могут уничтожить объекты GDI +.
Ответы
Ответ 1
Конечно, это имеет значение. Важно уничтожить все экземпляры классов, которые реализуют IDisposable, когда они больше не нужны. Классы определены IDisposable, когда они используют неуправляемые ресурсы, то есть ресурсы операционной системы, которые не обрабатываются .NET Framework.
Если вы не вручную удаляете объекты, то эти неуправляемые ресурсы не будут освобождены до тех пор, пока сборщик мусора не запустит финализатор объекта (и это произойдет, только если класс правильно выполнил Dispose pattern), что может быть очень запоздалым, поскольку сборщик мусора работает только тогда, когда он обнаруживает нехватку памяти. Таким образом, если вы не располагаете объектами, вы сохраняете ресурсы операционной системы, которые в противном случае могли бы использоваться другими приложениями.
Обсуждение этой темы можно найти здесь: http://agilology.blogspot.com/2009/01/why-dispose-is-necessary-and-other.html
Ответ 2
FWIW, в GDI есть "запасные" объекты. Когда вы создаете объект запаса, вы его не удаляете, потому что он "принадлежит" ОС.
Вероятно, вы уже знаете об объектах акций, но вот ссылка , которая идет на несколько деталей.
Я не знаю, есть ли аналогичные объекты "запаса" в GDI+. Я только что разыскал и не нашел ссылок на такие.
В качестве теста я написал небольшую программу WinForms с обратным вызовом таймера (установленным на огонь каждые 10 миллисекунд) следующим образом:
private void timer1_Tick(object sender, EventArgs e)
{
byte r = (byte)rnd.Next(0, 256);
byte g = (byte)rnd.Next(0, 256);
byte b = (byte)rnd.Next(0, 256);
System.Drawing.SolidBrush sb = new SolidBrush(Color.FromArgb(0,r,g,b));
}
Если я позволю ему запустить, он будет медленно потреблять память. Наблюдая за TaskManager (не самым точным способом оценить его), использование памяти имеет тенденцию расти (на моей машине, построенной с помощью .NET 4.0 VS2010, Release) около 20 тыс. Байт на обновление диспетчера задач (с максимальной скоростью обновления). Если я вызову Dispose на кисти, использование памяти, как правило, возрастет примерно на 8k за обновление диспетчера задач.
Вряд ли окончательный тест, но он, по-видимому, указывает на большее использование памяти с течением времени, если SolidBrush не удаляется. Интересно, что я не обрабатывал ни GDI Objects, ни вообще увеличился, поскольку тест проходил (в любом случае). Основываясь на прошлом опыте утечки ресурсов GDI, я ожидал увидеть, что GDI Objects растет, особенно в случае с не-Dispose.
В любом случае, может быть, это было информативно, может быть, и нет.
Ответ 3
Объекты должны удаляться сборкой мусора после того, как они вышли из сферы действия, если на что-то еще не ссылаются.
Самый простой способ продемонстрировать, происходит ли это или нет (без каких-либо изменений кода), - использовать большое количество этих элементов управления в форме и посмотреть, продолжает ли память, используемая приложением, оставаться стабильной.
Ответ 4
Он просто пропускает неуправляемые ресурсы (перо/кисть), пока соответствующий управляемый объект не будет возвращен сборщиком мусора. Таким образом, вы теряете несколько ручек, которые больше не используются впоследствии.
Это не слишком большая сделка, потому что Finalize()
вызовет Dispose()
, но освобождение неуправляемых ресурсов обычно должно выполняться раньше, чем позже.
Ответ 5
Мне нужно исправить некоторые утечки памяти в приложении, поэтому я занимаюсь некоторым расследованием. В двух словах, кажется, что в большинстве случаев вы избегаете этого с немного более высоким использованием памяти.
Ниже приведен патологический случай. Я наблюдаю за своим тестовым приложением в диспетчере задач (грубый я знаю) и наблюдал за столбцами памяти (Private Working Set), Handles, USER Objects и GDI. Клики Button1 вызывают более высокую загрузку памяти, чем нажатие кнопок Button2.
Если я нажимаю быстро и постоянно на Button1, я могу вызвать "System.OutOfMemoryException", когда Memory достигает 1,6 ГБ. Button2 никогда не поднимается выше, чем около 12Мб независимо от того, как безумно или упорно я нажимаю.
Я нахожусь на 64-битной машине win7, создавая приложение winforms client vs 2010..net 4. Очевидно, вы никогда не будете создавать миллионы и миллионы кистей...
С уважением Дэвид
private void button1_Click(object sender, EventArgs e) {
for (int i = 0; i < 500000; i++) {
SolidBrush b = new SolidBrush(Color.FromArgb(2, 32, 43, 128));
}
}
private void button2_Click(object sender, EventArgs e) {
for (int i = 0; i < 500000; i++) {
using (SolidBrush b = new SolidBrush(Color.FromArgb(2, 32, 43, 128))) {
}
}
}
Ответ 6
Кроме того, я бы сказал, что предпочтительнее использовать готовые коллекции:
System.Drawing.Pens.
а также
System.Drawing.Brushes.
если вы не настроите их особым образом.