Утечка памяти в WPF с производными текстовыми полями - PullRequest
4 голосов
/ 01 сентября 2011

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

Приложение построено с элементами управления вводом, полученными из 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>

Ответы [ 2 ]

3 голосов
/ 16 ноября 2011

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

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

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

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

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

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

1 голос
/ 15 ноября 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).

...