UWP开发入门(十三)——用Diagnostic Tool检查内存泄漏

  由于.NET的垃圾回收机制至关完善,一般状况下咱们是不须要关心内存泄漏的。问题人一但傻起来,连本身都会惧怕,几个页面跳啊跳的,内存蹭蹭的往上涨,拉都拉不住。这种时候咱们就须要冷静下来,泡一杯热巧克力。再打开Visual Studio 2015Diagnostic Tools,来检查下到底哪段代码出了问题。git

  咱们先建立一个简单的UWP工程,该工程只有2个几乎为空的PageMainPage只有两个按钮,分别用来跳转到SecondPage,以及调用GC.Collect()方法。而SecondPage就只有一个Goback用的按钮,同时在SecondPage的构造函数里建立了一个将近400MB的超大ArrayListgithub

<Page
    x:Class="EventMemoryLeak.MainPage"
…… >

    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" VerticalAlignment="Center">
        <Button Click="Button_Click">Go to second page</Button>
        <Button Click="Button_Click_1">Force GC</Button>
    </StackPanel>
</Page>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.Frame.Navigate(typeof(SecondPage));
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            GC.Collect();
        }
    }
<Page
    x:Class="EventMemoryLeak.SecondPage"
…… >

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Button Click="Button_Click">Go back to main page</Button>
    </Grid>
</Page>
    public sealed partial class SecondPage : Page
    {
        public ArrayList arrayList { get; set; }

        public SecondPage()
        {
            this.InitializeComponent();
            arrayList = new ArrayList(100000000);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.Frame.GoBack();
        }
    }

  在Visual Studio 2015Debug UWP程序时,会自动打开Diagnostic Tools的窗口(没打开也不要紧,能够经过Debug->Show Diagnostic Tools找到)。微信

  每从MainPage跳转SecondPage后,内存都会明显的增长。 ide

  

  在我写下上面这段话以后,再回到运行中的程序,在MainPage点击“Force GC“按钮后,CLR很给面子作了一次完全的回收,内存占用回到了程序刚打开的状态。这里须要说明的是,调用GC.Collect方法并不能保证当即回收全部引用计数为0的对象且释放全部内存。CLR会本身判断该怎么回收,回收多少,根本就是傲娇的小公举。函数

  

  那是否是说傲娇的Diagnostic Tools不靠谱呢?非也!首先调用GC.Collect方法,回收是必定会被执行的,一定会有一部分的对象被释放,这部分变化咱们能够经过Snapshot很好的进行观察(后面会介绍)。其次,若是确实须要进行比较完全的回收,根据我的经验,连续调用23GC.Collect方法,效果仍是很好的。再傲娇的小公举连续收到“在么”的微信,也会回复“呵呵,睡觉了”意思一下的。性能

  接下来咱们要故意制造严重的内存泄漏,并用Diagnostic Tools来进行观察。咱们增长一个Service层的类,并在SecondPage中监听Service层的事件。同时我将SecondPage建立的ArraryList400MB改成40MB,由于我主打轻薄的笔记本性能没法支撑。this

    public class FakeService
    {
        public static FakeService Instance = new FakeService();

        public event EventHandler ShowMeTheMoneyEvent;

        private FakeService() { }
    }
        public SecondPage()
        {
            this.InitializeComponent();
            arrayList = new ArrayList(10000000);
            FakeService.Instance.ShowMeTheMoneyEvent += Instance_ShowMeTheMoneyEvent;
        }

 

  

  这回你会发现,不管你怎么样GC(怎么感受这个名字有点污……算了我什么都不知道),内存都不会降低了。这是由于SecondPageFakeService所引用,FakeService又是静态的存活于整个APP生命周期的对象,因此SecondPage不再会被回收释放了。哎呀个人妈呀……spa

  先别急着叫,用Snapshot在比较一下内存对象,会有更可怕的事情发生。咱们从新运行该程序,在第一次运行到MainPage时,作一次Snapshot。反复的打开3SeconcdPage,再返回MainPage作第二次的SnapShot3d

 

  能够看到对象相对于第一次SnapShot仅增长了43个,但Heap Size已经惨不忍睹了。点击(+43)会打开详细的对象列表。通常状况下,我会在右上角填写命名空间来缩小观察的范围。咱们这里会惊讶的发现SecondPage对象,在3次打开该页面后,居然有3份重复的实例存在。code

  点击列表中的SecondPage一行,在屏幕下方的窗口中,会显示Path to Root的相关状况,能够看到SecondPage对象都由EventHandler关联到了FakeService对象上。

  至此,咱们经过Diagnostic Tools就找到了内存泄漏的缘由,处理方法也很简单,在离开页面时,取消对事件的监听就好了,这里咱们能够在页面的OnNavigateFrom方法里来作。

protected override void OnNavigatedFrom(NavigationEventArgs e)

        {

            base.OnNavigatedFrom(e);

            FakeService.Instance.ShowMeTheMoneyEvent -= Instance_ShowMeTheMoneyEvent;

        }

  本篇咱们简单的讨论如何使用Diagnostic Tools来观察内存对象,并就监听静态对象的事件引发的内存泄漏举例给出了解决方案。但愿可以抛砖引玉,引出许多真知灼见,最不济您也点个推荐呗。

  GayHub:

  https://github.com/manupstairs/UWPSamples/tree/master/UWPSamples/EventMemoryLeak

相关文章
相关标签/搜索