在 VS 的开发环境中,有内存性能分析的工具(VS 的菜单栏——> 调试——> 启动 Windows Phone性能分析)
可以分析应用的内存占用情况,但是通常只有在程序停止运行结束后,我们才能看到数据。但是有时候我们想在程序
运行过程中时事看到内存占用情况,比如在执行某个特定的方法后获取内存信息。这里我写了一个简单的方法,在程序
运行时,每隔四秒就把内存信息打印到屏幕上,为了在应用的所有页面都可以检查,我们使用之前写过的一个自定义 Toast
输出框(文章和代码工程)。
首先在页面的构造函数中,初始化一个 计时器:
// 初始化计时器,每隔四秒钟就打印当前应用所占内存、峰值、和设备总内存 dispacherTimer = new System.Windows.Threading.DispatcherTimer(); dispacherTimer.Interval = TimeSpan.FromSeconds(4); dispacherTimer.Tick += new EventHandler(dispacherTimer_Tick);
定义定时器调用的方法:
#region 内存使用情况 static string total = "DeviceTotalMemory"; static string current = "ApplicationCurrentMemoryUsage"; static string peak = "ApplicationPeakMemoryUsage"; static long totlaBytes; static long currentBytes; static long peakBytes; static System.Windows.Threading.DispatcherTimer dispacherTimer; void dispacherTimer_Tick(object sender, EventArgs e) { // 获取设备的总内存 totlaBytes = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(total); // 获取应用当前占用内存 currentBytes = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(current); // 获取内存占用的峰值 peakBytes = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(peak); // 使用自定义的 MyDialog 控件打印出来 MyDialog.ShowToastMessage(string.Format("当前:{0:F2}MB; 峰值:{1:F2}MB; 总:{2:F2}MB;", currentBytes / (1024 * 1024.0), peakBytes / (1024 * 1024.0), totlaBytes / (1024 * 1024.0))); } #endregion
在xaml 页面中添加两个按钮,分别用来启动和关闭内存信息检测,相应的 C# 函数:
// 取消内存信息打印,释放资源 private void btnStop_Click(object sender, RoutedEventArgs e) { dispacherTimer.Stop(); dispacherTimer.Tick -= new EventHandler(dispacherTimer_Tick); dispacherTimer = null; } // 开始向屏幕输出内存情况 private void btnStart_Click(object sender, RoutedEventArgs e) { if (dispacherTimer != null) { dispacherTimer.Start(); } }
显示截图:
内存占用的问题在 WP 的开发中是比较棘手的。因为市面上的 Lumia系列的手机,很多都是一体机,无论是
800 还是 920,它们在运行应用时,如果你的应用持续占用超过 120MB 的内存,那么它的发热情况是非常明显
的。而且导致发热的问题主要是内存的问题,并非 CPU 消耗的问题,而且在传统的 Silverlight 应用中,相对于
游戏应用,使用的数学运算等等消耗 CPU 的资源往往是微不足道的。
我在开发过程中,总结了内存占用的一些情况,其中最主要的是在页面中绘制的元素太多,或者页面的显示尺寸太
大,比如在一个 ListBox 中显示新闻,屏幕的高度是 800px,每屏显示 4条新闻,如果当前 ListBox 中共有24 条新闻,
那么该 ListBox 就是 6 屏 X 800px = 4800 px 的高度 !!!如果这 24条全部显示在页面中,内存占用是 60MB,
但是用户看到的有效信息只是屏幕中显示的 4条。所以把 ListBox 换成 LongListSelector (支持虚拟化和内存复用)
是很有必要的。如果想继续使用 ListBox 可以写一个算法(曾经写过),就是ListBox 只显示当前四条的新闻,其它
20条的新闻可以把它们的 Visibility 设置为 System.Windows.Visibility.Collapsed,当用户的手指划到那条新闻时,
再把那条新闻的该属性设置为 System.Windows.Visibility.Visible, 这样如果全部显示 24条新闻占用 60MB,那么
通过这个算法,内存只需要 25MB 左右。
通常导致内存增加,可能是程序中一些不再使用的对象得不到有效的释放,这些对象越来越多,从而导致内存泄露,
可能的原因:
1)静态对象的引用
2)事件没有与方法解除订阅
3)静态事件没有与方法解除订阅
4)非托管资源(实现了 IDispose 接口),没有调用 Dispose 方法
5)非托管资源的 Dispose 方法没有执行完成
另一个问题是,有的开发者在开放 WP8 的过程中,担心使用 async 和 await 关键字过多,会不会导致系统频繁创建
额外的线程过多,结果消耗很多系统资源。其实并非如此,因为在 .net 4.5 中引入的 async 和 await 关键字只是简化了
.net 4.0 中 Task 的创建和维护,通过使用同步编程的方式实现了异步线程的调用,并且 Task 在异步调用时,底层并非
创建了新的线程,而是使用的线程池技术,达到线程复用的目的。所以在 WP8 的开发中,尽可能的使用 async 和 await
因为它们不会增加应用过多占用系统的资源。
最后还有一点建议,就是在程序开发的时候,比如在应用中需要显示网页内容,一个选择是在应用中放一个 WebBrowser
控件,除非迫不得已,否则尽可能调用 Microsoft.Phone.Tasks.WebBrowserTask, 从而使用系统的 IE 浏览器打开网页内容
,因为 WebBrowser 控件显示网页内容时,所占用的内存也是相当大的,而且使用 IE 打开网页内容,不但系统会优化系统
资源,而且它所占用的内存并不会算入到应用占用的内存中去。
同理,相对于调用摄像头的 API(高级捕获 PhotoCaptureDevice 和 PhotoCamera ),最好使用系统任务:
Microsoft.Phone.Tasks.CameraCaptureTask。