zoukankan      html  css  js  c++  java
  • UWP开发入门(十六)——常见的内存泄漏的原因

      本篇借鉴了同事翔哥的劳动成果,在巨人的肩膀上把稿子又念了一遍。

      内存泄漏的概念我这里就不说了,之前《UWP开发入门(十三)——Diagnostic Tool检查内存泄漏》中提到过,即使有垃圾回收机制,写C#还是有可能发生内存泄漏。

      一般来说,以下两种情况会导致内存泄漏:

    1. 对象用完了但是没有释放资源
    1. 对象本身是做了清理内存的操作,但是对象内部的子对象没有成功释放资源

      下面就UWP开发中具体的实例来说明需要避免的写法

    • static/global的对象上注册了事件
    FakeService.Instance.ShowMeTheMoneyEvent += Instance_ShowMeTheMoneyEvent;

      比如我们有一个底层的FakeService,提供整个APP生命周期的数据和网络的访问。假设某个页面+=了这个FackServiceEvent,在离开页面时没有-=掉。那么该页面就无法被垃圾回收。

    合理的做法是在OnNavigatedFrom方法里,把事件反注册掉。

            protected override void OnNavigatedFrom(NavigationEventArgs e)
            {
                base.OnNavigatedFrom(e);
                FakeService.Instance.ShowMeTheMoneyEvent -= Instance_ShowMeTheMoneyEvent;
            }
    • DispatcherTimer事件未关闭

      这种情况就属于对象内部的属性未能被释放,假设页面内部存在Timer对象:

        public sealed partial class TimerPage : Page
        {
            private DispatcherTimer Timer { get; set; } = new DispatcherTimer();
    
            public ArrayList arrayList { get; set; }
    
            public TimerPage()
            {
                this.InitializeComponent();
                arrayList = new ArrayList(10000000);
                Timer.Tick += Timer_Tick;
                Timer.Interval = TimeSpan.FromSeconds(1);
                Timer.Start();
            }
    
            private void Timer_Tick(object sender, object e)
            {
                int count = 0;
                int.TryParse(TextBoxTimer.Text, out count);
                count += 1;
                TextBoxTimer.Text = count.ToString();
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                this.Frame.GoBack();
            }
    
            protected override void OnNavigatedFrom(NavigationEventArgs e)
            {
                base.OnNavigatedFrom(e);
                Timer.Stop();
            }
        }

      如果在离开页面之前,未调用Timer对象的Stop方法,也未-=Tick事件(这里Stop方法会自动-=Tick事件)。该页面就不能正常的回收。

      这里并不是说所有的Event都需要在OnNavigatedFrom方法中-=,例如Control本身的LoadedIsEnabledChanged等事件等并不会造成内存泄漏,反注册这些事件是为了避免事件的重复触发。而DispatcherTimer比较特殊,我理解它会把自己加到一个专门维护计时器的队列中,然后不停的触发Tick事件,如果没有Stop-=,就等于Timer一直引用了外部的对象,从而导致页面本身也无法回收。

    • Data Binding Memory Leak

      这一条在很多的文档上有所提及,很遗憾我没法通过Diagnostic Tools监测出来具体的泄漏,我猜测可能是很小规模的内存泄漏。但是避免的方式非常容易,只要平时写XAML注意一下就可以了。

      会出现问题的写法是以下两种:

    1. 未实现INotifyPropertyChanged的对象,而你又想监测Property变化
    2. 未实现INotifyCollectionChanged 接口的集合,而你又想监测Collection变化

      其实很好处理。如果想监测变化,就老老实实继承对应的接口。如果使用了普通的Property和集合,并且不想监测变化,一定记得Mode = OneTime

      当然如果属性本身是dependency property,就不存在内存泄漏的情况了。

            <!--内存泄漏,因为Children集合没有实现INotifyPropertyChanged来通知Count属性变化-->
            <TextBlock Text="{Binding ElementName=layoutRoot, Path=Children.Count}" />
            <!--不会内存泄漏,因为ActualWidth是依赖属性-->
            <TextBlock Text="{Binding ElementName=layoutRoot, Path=ActualWidth}" />
            <!--不会内存泄漏,因为Mode = OneTime-->
            <TextBlock Text="{Binding ElementName=layoutRoot, Path=Children.Count, Mode = OneTime}" />
    • 非托管资源的释放

      这个都非常熟悉,不多说了。主要是通过using语句,或者在try { … } finally { … }中调用Dispose或者Close方法来释放非托管资源。

  • 相关阅读:
    夜神安卓模拟器
    Jmeter分布式压力测试环境配置
    Jmeter接口测试数组变量传值
    Jmeter测试JDBC
    BeanShell PreProcessor数据base64加密
    数据库锁表查看与解锁
    Python对数据库进行操作
    Jmeter正则表达式提取器详解
    将博客搬至CSDN
    web开发学习笔记(四)Ajax的使用方法
  • 原文地址:https://www.cnblogs.com/manupstairs/p/5617940.html
Copyright © 2011-2022 走看看