zoukankan      html  css  js  c++  java
  • Win2D 官方文章系列翻译

    本文为个人博客备份文章,原文地址:

    http://validvoid.net/win2d-avoiding-memory-leaks/

    在托管 XAML 应用中使用 Win2D 控件时,必须谨慎处理对象引用计数,以免控件不能被垃圾回收器回收。

    内存泄漏的发生条件

    • 你正在通过 C# 等 .Net 语言 (非原生 C++)使用 Win2D
    • 你使用了以下任一 Win2D 控件:
      • CanvasControl
      • CanvasVirtualControl
      • CanvasAnimatedControl
      • CanvasSwapChainPanel
    • 你订阅了 Win2D 控件的事件(如 DrawCreateResourcesSizeChanged...)
    • 你的应用在多个 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(1);
    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 事件中解除所有事件处理的订阅)。

  • 相关阅读:
    贝叶斯公式推导
    三种常量池
    SpringCloud使用Feign实现服务间通信
    springCloud配置本地配中心SpringCloudConfig
    SpringApplication执行流程
    调用shutdown.sh后出现could not contact localhost8005 tomcat may not be running报错问题
    TCP协议详解
    web.xml配置说明
    第一份offer
    博客CSS
  • 原文地址:https://www.cnblogs.com/validvoid/p/win2d-avoiding-memory-leaks.html
Copyright © 2011-2022 走看看