Утечка памяти WPF с производными текстовыми полями

В одном из моих приложений у меня проблема с производительностью, которую я не могу решить:

Приложение построено с элементами управления вводом, производными от класса TextBox, имеющими собственный ControlTemplate в Themes\Generic.xaml.

Моя проблема в том, что эти элементы управления не будут выпущены после того, как они больше не будут использоваться. Если я посмотрю на них с помощью SciTech MemoryProfiler, то увижу, что они удерживаются экземпляром System.Windows.Documents.TextEditor, а экземпляр TextEditor удерживается через очередь финализатора.
Профилировщик памяти прикрепляет предупреждение к экземпляру TextEditor, говоря: «Экземпляр косвенно укоренен очередью финализатора».
Кто-нибудь знает, что здесь происходит? Разве это не разрешено напрямую из TextBox? Или я забыл что-то важное реализовать?

Дополнительная информация по реализации
Реализация некоторых из этих производных элементов управления очень проста. В конструкторе класса метаданные DefaultStyleKeyProperty переопределяются, и никакие обработчики событий не присоединяются к элементам, содержащимся в шаблоне элемента управления. Что-то типа:

public class MyDerivedTextBox : TextBox{

   static MyDerivedTextBox(){
       DefaultStyleKeyProperty.OverrideMetadata(typeof(MyDerivedTextBox), new FrameworkPropertyMetadata(typeof(MyDerivedTextBox)));
   }

}

(Упрощенный) стиль выглядит примерно так:

<Style TargetType="{x:Type myApp_controls:MyDerivedTextBox}">
     <Setter Property="SnapsToDevicePixels" Value="True"/>
     <Setter Property="UndoLimit" Value="1"/>
     <Setter Property="FocusVisualStyle" Value="{x:Null}"/>        
     <Setter Property="Template">
         <Setter.Value>
            <ControlTemplate TargetType="{x:Type myApp_controls:MyDerivedTextBox }">
                <Border Name="Border" ... >
                     <ScrollViewer Margin="1" x:Name="PART_ContentHost" />
                </Border>
          </Setter.Value>
      </Setter>
</Style>

person HCL    schedule 01.09.2011    source источник


Ответы (2)


Очередь финализатора
Ответ на вопрос об очереди финализатора заключается в том, что наблюдаемый эффект не является постоянным: финализация просто не была завершена в тот момент, когда я анализировал память. Проблема здесь в том, чтобы понять инструмент (и среду), который я использовал.

Утечка памяти
Однако сама утечка была настоящей проблемой, и оказалось, что это то же самое я также наблюдал в других местах (в том же приложении). Привязки к CLR-свойствам классов, не реализующих INotifyPropertyChanged! http://support.microsoft.com/kb/938416/en-us

Это был один из моих первых проектов WPF, и со временем он превратился в огромное приложение. В то время, когда я начинал, я не знал, что WPF имеет проблемы с привязками, упомянутыми выше, и во время разработки я пытался сделать как можно больше с привязкой данных, но не заботился о целевых объектах. Теперь, когда приложение стало таким большим и количество клиентов резко увеличилось, эти проблемы с памятью выявились (и привели к очень странным эффектам).

После решения наиболее проблемных привязок также резко уменьшился эффект очереди финализатора. Похоже, что раньше утечки памяти приводили к отложенному выполнению финализации объекта (это только предположение, глубже в GC-поведении не копался).

Производные текстовые поля
Я создал небольшой пример проекта с такими производными элементами управления текстовыми полями, используя их в некоторых стресс-тестах в профилировщике памяти. Насколько я могу судить по своим наблюдениям за тестовым проектом, вывод из текстовых полей, как я это делал, работает отлично.

Fazit
Я могу только подчеркнуть важность проверки целевых объектов Bindings во время создания приложения. В противном случае потребуется много работы по выявлению узких мест в приложении. Я надеюсь, что это объяснение поможет кому-то не совершать тех же ошибок, что и я.

person HCL    schedule 16.11.2011

Не уверен, что это что-то изменит, но вместо статического конструктора под вашим контролем вы пробовали что-то вроде:

public MyDerivedTextBox() 
{
  this.DefaultStyleKey = typeof(MyDerivedTextBox);
}

Это схема, с которой я больше знаком. Возможно, MetaDataOverride делает что-то шаткое.

Кроме того, одна вещь, которую я заметил при некоторых проблемах с памятью Silverlight, это наличие необъяснимых AutomationPeers, вызванных службами ввода Tablet PC (см. http://www.wintellect.com/CS/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx).

person SergioL    schedule 15.11.2011
comment
+1 Спасибо за ваш ответ. В конце концов, это была проблема, с которой я уже сталкивался в другом месте: привязки данных к свойствам объектов, которые не реализуют INotifiyPropertyChanged и не являются производными от DependencyObject. В то время, когда я начал проект, я не знал, насколько плохо это может быть в больших проектах. Что касается производных от TextBox, теперь я могу точно сказать, что это работает нормально. - person HCL; 16.11.2011