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

  因为.NET的垃圾回收机制相当完善,通常情况下我们是不需要关心内存泄漏的。问题人一但傻起来,连自己都会害怕,几个页面跳啊跳的,内存蹭蹭的往上涨,拉都拉不住。这种时候我们就需要冷静下来,泡一杯热巧克力。再打开Visual Studio 2015的Diagnostic Tools,来检查下到底哪段代码出了问题。

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

<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 2015中Debug UWP程序时,会自动打开Diagnostic Tools的窗口(没打开也没关系,可以通过Debug->Show Diagnostic Tools找到)。

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

  

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

  

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

  接下来我们要故意制造严重的内存泄漏,并用Diagnostic Tools来进行观察。我们增加一个Service层的类,并在SecondPage中监听Service层的事件。同时我将SecondPage创建的ArraryList从400MB改为40MB,因为我主打轻薄的笔记本性能无法支撑。

    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(怎么感觉这个名字有点污……算了我什么都不知道),内存都不会下降了。这是因为SecondPage被FakeService所引用,FakeService又是静态的存活于整个APP生命周期的对象,所以SecondPage再也不会被回收释放了。哎呀我的妈呀……

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

 

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

  点击列表中的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

时间: 2024-12-17 02:33:13

UWP开发入门(十三)——用Diagnostic Tool检查内存泄漏的相关文章

使用Debug Diagnostic Tool排除内存泄漏故障

在我之前的博文中(SQL Server内存泄漏),我解释了如何使用“!heap”命令识别哪个模块泄漏了内存.有时我们使用“!d”命令来找到模型或者使用搜索内存命令(s)不能通过显示内存找到原因. 在这种情况下,我们可以使用Debug Diagnostic Tools或者UMDH来跟踪内存泄漏.这篇博文解释了如何使用Debug Diagnostics Tools来识别内存泄漏. 下载并安装Debug Diagnostic Tools从http://www.microsoft.com/en-us/d

UWP开发入门(十六)——常见的内存泄漏的原因

本篇借鉴了同事翔哥的劳动成果,在巨人的肩膀上把稿子又念了一遍. 内存泄漏的概念我这里就不说了,之前<UWP开发入门(十三)——用Diagnostic Tool检查内存泄漏>中提到过,即使有垃圾回收机制,写C#还是有可能发生内存泄漏. 一般来说,以下两种情况会导致内存泄漏: 对象用完了但是没有释放资源 对象本身是做了清理内存的操作,但是对象内部的子对象没有成功释放资源 下面就UWP开发中具体的实例来说明需要避免的写法 从static/global的对象上注册了事件 FakeService.Ins

iOS开发 如何检查内存泄漏

本文转载至 http://mobile.51cto.com/iphone-423391.htm 在开发的时候内存泄漏是不可避免的,但是也是我们需要尽量减少的,因为内存泄漏可能会很大程度的影响程序的稳定性!!!最近一阵在疯狂的查询内存泄漏问题,总结有几种方法供大家参考. AD:2014WOT全球软件技术峰会北京站 课程视频发布 在开发的时候内存泄漏是不可避免的,但是也是我们需要尽量减少的,因为内存泄漏可能会很大程度的影响程序的稳定性!!!最近一阵在疯狂的查询内存泄漏问题,总结有几种方法供大家参考.

用mtrace检查内存泄漏

http://blog.csdn.net/ixidof/article/details/6638066内存泄漏检查方法(for Linux) 如果你更想读原始文档, 请参考glibc info的"Allocation Debugging" 一章 (执行info libc);glibc提供了一个检查内存泄漏的方法, 前提是你的程序使用glibc的标准函数 分配内存(如malloc, alloc...): 1. 在需要内存泄漏检查的代码的开始调用void mtrace(void) (在mc

UWP开发入门(二十三)——WebView

本篇讨论在UWP开发中使用WebView控件时常见的问题,以及一些小技巧. WebView是实际开发中常用的控件,很多大家抱怨的套网页的应用都是通过WebView来实现的.这里要澄清一个问题,套网页的应用并不一定是差的应用,很多网页采用了响应式设计,假设网页不存在复杂的交互,提取网页的正文部分嵌入WebView,可以说方便快捷省时省力.比如亚马逊.驴妈妈这些UWP APP都还挺不错的,京东那个网页就套的比较差了…… WebView最为简单的用法如下: <WebView Source="ht

UWP开发入门(七)——下拉刷新

本篇意在给这几天Win10 Mobile负面新闻不断的某软洗地,想要证明实现一个简单的下拉刷新并不困难.UWP开发更大的困难在于懒惰,缺乏学习的意愿.而不是“某软连下拉刷新控件都没有”这样的想法. 之前我也没有进行过下拉刷新的研究.于是先去google了几篇blog学习了一下,然后再看了某软官方的Sample.(同学们啊官方有下拉刷新的Sample啊!就在Git上啊!不要钱无门槛啊!)学习之后发现实现的方式大体分为两类. 一类是以某软Sample和博客园MS-UAP封装的PullToRefres

UWP开发入门(一)——SplitView

接下来会写一个UWP(Universal Windows Platform)开发入门的系列,自己学习到哪里,有什么心得总结,就会写到哪里.本篇对适用于顶层导航的SplitView控件展开讨论. 首先SplitView是Win10 UWP新增的控件,以前虽然可以通过DockPanel模拟出类似的效果,但又哪里及得上M$原生支持的SplitView快捷方便呢. 至于为什么说SplitView适合顶层导航,可以参考目前尚为数不多的UWP APP,比如微博.QQ和网易等Win10 APP,基本都是通过S

UWP开发入门(十一)——Attached Property的简单应用

UWP中的Attached Property即附加属性,在实际开发中是很常见的,比如Grid.Row: <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition>

UWP开发入门(二十一)——保持Ui线程处于响应状态

GUI的程序有时候会因为等待一个耗时操作完成,导致界面卡死.本篇我们就UWP开发中可能遇到的情况,来讨论如何优化处理. 假设当前存在点击按钮跳转页面的操作,通过按钮打开的新页面,在初始化过程中存在一些耗时的操作. public void OnNavigatedTo(object obj) { var watch = new Stopwatch(); Debug.WriteLine("---------------Start"); watch.Start(); //假设耗时1秒 DoBu