Win2D 官方文章系列翻译 - 避免内存泄漏
本文为个人博客备份文章,原文地址:
http://validvoid.net/win2d-avoiding-memory-leaks/
在托管 XAML 应用中使用 Win2D 控件时,必须谨慎处理对象引用计数,以免控件不能被垃圾回收器回收。
内存泄漏的发生条件
- 你正在通过 C# 等 .Net 语言 (非原生 C++)使用 Win2D
 - 你使用了以下任一 Win2D 控件:
- CanvasControl
 - CanvasVirtualControl
 - CanvasAnimatedControl
 - CanvasSwapChainPanel
 
 - 你订阅了 Win2D 控件的事件(如 
Draw,CreateResources,SizeChanged...) - 你的应用在多个 XAML 页面中前进后退进行导航
 
如果以上情况全部满足,那么一个引用计数循环会阻止垃圾回收器回收 Win2D 控件。 在应用每次导航到一个新页面时,新的 Win2D 资源都会进行分配,而老资源则一直未被释放,从而造成内存泄漏。要避免这一情况,你必须手动添加代码打破引用计数循环。
如何修复
要打破引用计数循环以便使也页面能够被垃圾回收,你需要:
- 订阅包含 Win2D 控件的 XAML 页面的 
Unloaded事件。 - 在 
Unloaded事件的处理逻辑中,调用 Win2D 控件的RemoveFromVisualTree方法。 - 在 
Unloaded事件的处理逻辑中,(通过将控件实例设为 null)释放任何对 Win2D 控件的直接引用。 
示例代码:
void page_Unloaded(object sender, RoutedEventArgs e)
{
this.canvas.RemoveFromVisualTree();
this.canvas = null;
}
完整示例可以参见示例项目中的 demo 页面。
测试循环泄露
要测试你的应用是否正确打破了引用计数循环,可以在包含 Win2D 控件的页面里添加析构函数:
~MyPage()
{
System.Diagnostics.Debug.WriteLine("~" + GetType().Name);
}
在应用的 App 构造函数中添加一个定时器以使垃圾回收定期执行:
var gcTimer = new DispatcherTimer();
gcTimer.Tick += (sender, e) => { GC.Collect(); };
gcTimer.Interval = TimeSpan.FromSeconds();
gcTimer.Start();
之后可启动应用进行调试,导航到页面,再导航到其它页面。如果全部引用循环被正确打破,一两秒内你就能在 Visual Studio 的输出面板看到 Debug.WriteLine 输出的信息。
注意
调用 GC.Collect 方法具有破坏性,会降低性能。因此一旦你完成内存泄漏测试,请务必移除此测试代码。
关键细节
当对象 A 引用了对象 B,同时对象 B 也引用了对象 A时,一个引用循环就会发生。当对象 A 引用对象 B, 对象B 引用对象 C,而对象C 引用对象A 等情况亦然。
当订阅一个 XAML 控件的事件时,这种引用循环不可避免:
- XAML 页面承载了全部其所包含控件的引用
 - 控件承载了所有订阅其事件委托的引用
 - 每个委托承载了其目标实例的引用
 - 每个事件处理程序都是 XAML 页面类的实例方法,所以他们的目标实例又指向回 XAML 页面从而形成一个引用循环
 
如果以上引用过程中的所有对象都是 .Net 实现的,则引用循环并不会成问题。因为 .Net 能够自动进行垃圾回收,即便对象形成了引用循环,垃圾回收算法也能够识别并回收它们。
不同于 .Net, C++ 通过引用计数管理内存,它不能自动探测并回收对象引用循环。尽管有此限制, C++ 应用使用 Win2D 是没有问题的。因为 C++ 的事件处理程序默认承载对象实例的弱引用而非强引用。 因此页面引用控件,控件引用事件处理委托,而委托并没有引用回页面,因此不会产生循环。
而当一个 .Net 应用使用 Win2D 这类 C++ WinRT 组件时问题就出现了:
- XAML 页面是应用的一部分,因此使用垃圾回收机制
 - Win2D 控件是由 C++ 实现的,因此使用引用计数机制
 - 事件处理委托是应用的一部分,因此使用垃圾回收机制并承载对目标示例的强引用
 
一个引用循环由此产生,而没有使用 .Net 垃圾回收机制的 Win2D 对象也参与了其中。 这意味着垃圾回收器无法查看整个引用链,因而它也无法探测和回收对象。当这一情况发生时,则必须通过显式打破引用循环来解决问题。一个方法是释放所有页面对控件的引用(如上文推荐那样);另外也可以找到所有可能指向回页面的事件处理委托,释放掉控件对这些委托的引用(在页面的 Unloaded 事件中解除所有事件处理的订阅)。
Win2D 官方文章系列翻译 - 避免内存泄漏的更多相关文章
- Win2D 官方文章系列翻译 - 处理设备丢失
		
本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-handling-device-lost/ “设备丢失”是指 GPU 设备失效无法继续进行渲染的情况.GPU ...
 - Win2D 官方文章系列翻译 - DPI (每英寸点数)和 DIPs(设备独立像素)
		
本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-dpi-dips/ 本文旨在解释物理像素与设备独立像素(DIPs, device independent pi ...
 - Win2D 官方文章系列翻译 - 幕后绘制
		
本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-offscreen-drawing/ 应用有时需要将图形绘制到并不立即显示的目标上.此类绘制动作被称作“幕后绘 ...
 - Win2D 官方文章系列翻译 - 预乘 Alpha
		
本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-premultiplied-alpha/ 在计算机绘图中有两种表示颜色值不透明度的方法.Win2D 中两种方法 ...
 - Win2D 官方文章系列翻译 - 像素格式
		
本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-pixel-formats/ DirectXPixelFormat 枚举 包含了 Direct3D 和 DXG ...
 - Win2D 官方文章系列翻译 - 与 Direct2D 互操作
		
本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-interop-with-direct2d/ Win2D 作为 Direct2D 的上层实现,支持与其进行双向 ...
 - Win2D 官方文章系列翻译 - 调整控件分辨率
		
本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-choosing-control-resolution/ 本文旨在讲解如何配置 Win2D XAML 控件使用 ...
 - Win2D 入门教程 VB 中文版 - 防止内存泄漏
		
避免内存泄漏 本文从微软官方文档翻译 http://microsoft.github.io/Win2D/html/RefCycles.htm 如果文档有问题,可以在 https://github.co ...
 - 18.一篇文章,从源码深入详解ThreadLocal内存泄漏问题
		
1. 造成内存泄漏的原因? threadLocal是为了解决对象不能被多线程共享访问的问题,通过threadLocal.set方法将对象实例保存在每个线程自己所拥有的threadLocalMap中,这 ...
 
随机推荐
- DIV中的垂直居中
			
<div style="border:0px #ff0000 solid; width:100px;height:380px; line-height:380px; float:lef ...
 - FastReport使用一——简介
			
一:FastReport Designer用法简介 1.使用FastReport.Net4文件夹下的Designer.exe进行模版的创建工作 图1 图1中,右键删除除==>数据区 之外的其他区 ...
 - 关于mysql数据库在输入密码后,滴的一声直接退出界面的解决办法
			
转自:http://www.2cto.com/database/201412/361751.html 网上搜索到的解决办法: 1.找到mysql安装目录下的bin目录路径.2.打开cmd,进入到bin ...
 - 64. Minimum Path Sum
			
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which ...
 - class 文件与dex文件区别 (dvm与jvm区别)及Android DVM介绍
			
区别一:dvm执行的是.dex格式文件 jvm执行的是.class文件 android程序编译完之后生产.class文件,然后,dex工具会把.class文件处理成.dex文件,然后把资源文件和 ...
 - iphone dev 入门实例7:How to Add Splash Screen in Your iOS App
			
http://www.appcoda.com/how-to-add-splash-screen-in-your-ios-app/ What’s Splash Screen? For those who ...
 - (VS) TFS lost mapping suddenly.
			
家里的网络不是很稳定.今天突然发现 TFS 上所有的 mapping都突然没有了. 尝试去remapping,在Source Control Explorer 中右击源文件,然后选择 Advanced ...
 - (C#) 判断相等?
			
值类型直接用 == 号判断就好. 但是对于引用类型,需要实现IComparable 接口,或者重写 Equal 方法,来实现自己的比较目的. 因为对于引用类型,==号比较的是入口地址,对于同一个cla ...
 - python (3)简单语法:字符串(strip函数),数据类型
			
一:字符串重复,索引,切片(字符串命令strip) 函数原型strip 声明:s为字符串,rm为要删除的字符序列 s.strip(rm) 删除s字符串中开头.结尾处,位于 rm删除序列的 ...
 - JAVA 构造方法之间的调用
			
this:看上去,用来区分局部变量和成员变量的情况this:就是代表本类对象,this代表它所在方法所属对象的引用构造方法之间的调用只能通过this语句来完成构造方法之间进行调用时this语句只能出现 ...