我想知道是否存在与CroppedBitmap和RenderTargetBitmap相关的.NET EventHandler泄漏问题。这让我深受困扰!
我的WPF应用程序以60fps的速度运行一个始终运行的渲染调用。我发现随着时间的推移,正在呈现的内容变得越来越慢。
经过大量调查,包括在Visual Studio中进行分析和使用ANTS内存分析器,我将其缩小到使用CroppedBitmap。
我有一些简单的问题演示代码,其中包括...
RenderTargetBitmap srcBitmap = new RenderTargetBitmap((int)scaledWidth, (int)scaledHeight, 96, 96, PixelFormats.Default);
RenderTargetBitmap destBitmap = new RenderTargetBitmap((int)scaledWidth, (int)scaledHeight, 96, 96, PixelFormats.Default);
<code loop>
{...
DrawingVisual DV = new DrawingVisual();
DrawingContext DC = DV.RenderOpen();
var srcRec = new Int32Rect(x,y,w,h);
var srcCrop = new CroppedBitmap(srcBitmap, srcRec);
var destRec = new Rect(.....);
DC.DrawImage(srcCrop, destRec);
DC.Close();
destBitmap.Render(DV);
DC = null;
DV = null;
...}
使用srcBitmap调用CroppedBitmap之后,会在srcBitmap上添加一个事件处理程序。具体来说,将CroppedBitmap.Source = srcBitmap设置为真时将添加此事件处理程序。
CroppedBitmap具有DownloadCompleted、DownloadFailed、DownloadProgress事件。我想猜测是DownloadCompleted事件。
通过检查事件处理程序数量(使用srcBitmap/RenderTargetBitmap上的 _downloadEvent 属性),每次调用CroppedBitmap时这个数量确实会增加一个,而且永远不会减少。我只能得出结论:CroppedBitmap正在向RenderTargetBitmap添加一个从未被删除的事件处理程序。运行一段时间后,RenderTargetBitmap上就会积累成千上万个事件处理程序,我认为这正是导致我的速度降低的原因。
我真的不知道该怎么办!图像被设置在简单的Image控件上,没有什么特别的。
我尝试着从发生这种情况的地方开始一步一步地跟踪到 .net 框架的深处,但当调试器开始进入优化过的 .net 库时,我就迷失了方向,无法继续挖掘。
位图的呈现很简单...
Content.Source = srcBitmap;
....
<Image x:Name="Content" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RenderOptions.BitmapScalingMode="LowQuality" Stretch="None" IsHitTestVisible="False"></Image>
为了明确起见,例如代码可以在没有渲染、没有绘制图像等情况下运行 - 只需要新的CroppedBitmap...代码才能显示这种行为。其他行以上是为了完整性而包含的。没有任何区别。
上面的示例是针对.NET Framework 4.7.2运行的,以确保最新版本存在此问题。
我还尝试了冻结CroppedBitmap,以防万一会有什么不同。显然没有。事实上,调用srcCrop.Freeze导致srcBitmap.isFrozen为true!嗯.....所以似乎CroppedBitmap对其被裁剪的位图进行了修改?
在上述方面找不到任何信息、帮助、文章等 :(
请注意,这不是RenderTargetBitmap泄漏内存,在支持论坛中您看到提及的是另外的问题。运行此代码而没有CroppedBitmap时它很好。我在几个地方使用RenderTargetBitmaps,所有的都很好(我已经按照那篇文章中描述的方式考虑了内存使用率的增加,并且我已经调用了GC.Collect等)。我还重复使用我的RenderTargetBitmaps,因此它们只被创建一次。(可能是为什么当应用多个CroppedBitmap时,它们的事件处理程序堆积起来的原因)。
我尝试使用反射器获取正在创建的事件处理程序,以便我可以手动删除它们,但是(尽管互联网上有各种示例)无法找到它们隐藏的位置。一定有某个地方!
任何人有什么想法可以修复这个问题吗?
提前感谢您的帮助...