在我的一个应用程序中,我遇到了无法解决的性能问题:
该应用程序是使用派生自 TextBox
类的输入控件构建的,在 Themes\Generic 中有自己的 ControlTemplate .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>
In one of the apps of mine I have a performance problem I can’t resolve:
The app is built with input controls derived from the TextBox
-class, having their own ControlTemplate in Themes\Generic.xaml
.
My problem is, that these controls will not be released after they are no more used. If I look at them with SciTech MemoryProfiler, I see that they are hold by an instance of System.Windows.Documents.TextEditor
and the TextEditor
-instance is hold through the finalizer queue.
Memory profiler attaches a warning to the TextEditor
-instance, saying “Instance indirectly rooted by finalizer queue”.
Has anyone an idea what’s going on here? Is it not allowed to derive directly from TextBox? Or have I forgotten something important to implement?
Additional information for the implementation:
The implementation of some of these derived controls is very simple. In class constructor, the DefaultStyleKeyProperty’s metadata is overridden and no event handlers are attached to elements contained in the control template. Something like:
public class MyDerivedTextBox : TextBox{
static MyDerivedTextBox(){
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyDerivedTextBox), new FrameworkPropertyMetadata(typeof(MyDerivedTextBox)));
}
}
The (simplified) style looks something like:
<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)
终结器队列
关于终结器队列的问题的答案是,观察到的效果不是恒定的:在我分析内存时,终结根本就没有完成。这里的问题是了解我所使用的工具(和环境)。
内存泄漏
然而,泄漏本身是一个真正的问题,事实证明,它是相同的我还在其他位置(在同一个应用程序中)观察到了这一点。 绑定到未实现 INotifyPropertyChanged 的类的 CLR 属性! http://support.microsoft.com/kb/938416/en-us
这是我的第一个 WPF 项目之一,同时它已发展成为一个巨大的应用程序。在我开始时,我并没有意识到 WPF 在上述绑定方面存在问题,并且在开发过程中我尝试尽可能多地进行数据绑定,但并不关心目标对象。现在,当应用程序变得如此庞大并且客户端数量急剧增加后,这些内存问题就暴露出来了(并导致了非常奇怪的影响)。
解决了最有问题的绑定后,终结器队列的效果也大大降低了。看来之前,内存泄漏导致了对象终结的延迟执行(这只是一个假设,我没有更深入地研究 GC 行为)。
派生文本框:
我使用此类派生的文本框控件创建了一个小示例项目,并在内存分析器内的一些压力测试中使用它们。据我对测试项目的观察可以说,像我一样从 TextBoxes 派生效果非常好。
法吉特
我只能强调在应用程序创建过程中检查 Bindings 的目标对象的重要性。否则,识别应用程序中的泄漏点将需要大量工作。我希望这个解释可以帮助别人不要犯和我一样的错误。
Finalizer queue
The answer for the question about the finalizer queue is, that the observed effect is not a constant one: The finalization was simply not finished at the point I have analyzed the memory. The problem here is to understand the tool (and the environment) I have used.
Memory Leak
The leak itselfs however was a real problem and it turned out, that it was the same thing I also observed at other positions (in the same app). Bindings to CLR-properties of classes not implementing INotifyPropertyChanged! http://support.microsoft.com/kb/938416/en-us
It was one of my first WPF projects and in the meantime it grew to a huge application. At the time I started, I was not aware, that WPF has problems with bindings mentioned above, and during development I have tried to do as much as possible with data binding but didn't care about the target objects. Now after the app has grown so big and the number of clients has increased dramatically, those memory problems came to light (and led to very strange effects).
After resolving the most problematic bindings, also the effect with the finalizer queue has been decreased drastically. It seems that before, the memory leaks had led to a deferred execution of the object finalization (this is only an assumption, I have not digged deeper into GC-behaviour).
Derived TextBoxes:
I have created a little sample project with such derived text-box controls, using them in some stresstests within memory profiler. As far as I can say from my observation of the test project, derive from TextBoxes as I did works perfectly well.
Fazit
I can only emphasize the importance of checking the target objects of Bindings during application creation. Otherwise, it will be a lot of work to identify the leaking spots in an application. I hope this explanation helps someone not doing the same mistakes as I did.
不确定它是否会改变任何东西,但是您是否尝试过类似的操作,而不是控制中的静态构造函数:
这是我更熟悉的模式。也许 MetaDataOverride 正在做一些奇怪的事情。
顺便说一句,我确实注意到一些 Silverlight 内存问题,即 Tablet PC 输入服务带来的无法解释的 AutomationPeers 的存在(请参阅 http://www.wintellect.com/CS/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx)。
Not sure if it will change anything, but instead of the static constructor in your control, have you tried something like:
That's the pattern I'm more familiar with. Perhaps the MetaDataOverride is doing something wonky.
As an aside, one thing I did notice with some Silverlight memory issues, was the presence of unexplained AutomationPeers brought on by Tablet PC Input Services (see http://www.wintellect.com/CS/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx).