如何在WP7中实时监控内存使用量
在windows phone 7 里开发的程序在运行过程中如果内存使用超过90M程序就会自动退出,但是在模拟器上没有这个限制,因此必须有方法帮助你在调试程序的时候时刻监控内存占用,否则即使在模拟器上没问题,一旦到了真机上就可能会出问题,最近在开发中我就遇到了这个问题,经过摸索,发现phone7提供了DeviceExtendedProperties可用于取得内存使用情况。
办法一:在app.xaml.cs里加入一个timer, 设置每间隔一段时间输出内存占用值到控制台:
1.在app.xaml.cs里添加下列函数:
void timer_Tick(object sender, EventArgs e)
{ //GC.GetTotalMemory(true);
long deviceTotalMemory = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("DeviceTotalMemory");
long applicationCurrentMemoryUsage = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
long applicationPeakMemoryUsage = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToLongTimeString());
System.Diagnostics.Debug.WriteLine("Device Total : " + deviceTotalMemory.ToString());
System.Diagnostics.Debug.WriteLine("App Current : " + applicationCurrentMemoryUsage.ToString());
System.Diagnostics.Debug.WriteLine("App Peak : " + applicationPeakMemoryUsage.ToString());
}
2。在构造函数public App{}里添加 :
System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000d);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
办法二:
使用MemoryDiagnosticsHelper.cs类,下面是源代码,将此文件添加到工程中。
然后将MemoryDiagnosticsHelper.Start(TimeSpan.FromMilliseconds(500), true); 这一句添加到构造函数public App{}里
if (System.Diagnostics.Debugger.IsAttached)
{
...
MemoryDiagnosticsHelper.Start(TimeSpan.FromMilliseconds(500), true);
...
}
用模拟器debug你的程序,手机界面右侧边会多出一条显示实时内存状态的信息。

using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using Microsoft.Phone.Info;
using System.Diagnostics;
using System.Collections.Generic;
namespace MemoryDiagnostics
{
/// <summary>
/// Helper class for showing current memory usage
/// </summary>
public static class MemoryDiagnosticsHelper
{
static Popup popup;
static TextBlock currentMemoryBlock;
static TextBlock peakMemoryBlock;
static DispatcherTimer timer;
static bool forceGc;
const long MAX_MEMORY = 90 * 1024 * 1024; // 90MB, per marketplace
static int lastSafetyBand = -1; // to avoid needless changes of colour
const long MAX_CHECKPOINTS = 10; // adjust as needed
static Queue<MemoryCheckpoint> recentCheckpoints;
static bool alreadyFailedPeak = false; // to avoid endless Asserts
/// <summary>
/// Starts the memory diagnostic timer and shows the counter
/// </summary>
/// <param name="timespan">The timespan between counter updates</param>
/// <param name="forceGc">Whether or not to force a GC before collecting memory stats</param>
[Conditional("DEBUG")]
public static void Start(TimeSpan timespan, bool forceGc)
{
if (timer != null)
throw new InvalidOperationException("Diagnostics already running");
MemoryDiagnosticsHelper.forceGc = forceGc;
recentCheckpoints = new Queue<MemoryCheckpoint>();
StartTimer(timespan);
ShowPopup();
}
/// <summary>
/// Stops the timer and hides the counter
/// </summary>
[Conditional("DEBUG")]
public static void Stop()
{
HidePopup();
StopTimer();
recentCheckpoints = null;
}
/// <summary>
/// Add a checkpoint to the system to help diagnose failures. Ignored in retail mode
/// </summary>
/// <param name="text">Text to describe the most recent thing that happened</param>
[Conditional("DEBUG")]
public static void Checkpoint(string text)
{
if (recentCheckpoints == null)
return;
if (recentCheckpoints.Count >= MAX_CHECKPOINTS - 1)
recentCheckpoints.Dequeue();
recentCheckpoints.Enqueue(new MemoryCheckpoint(text, GetCurrentMemoryUsage()));
}
/// <summary>
/// Recent checkpoints stored by the app; will always be empty in retail mode
/// </summary>
public static IEnumerable<MemoryCheckpoint> RecentCheckpoints
{
get
{
if (recentCheckpoints == null)
yield break;
foreach (MemoryCheckpoint checkpoint in recentCheckpoints)
yield return checkpoint;
}
}
/// <summary>
/// Gets the current memory usage, in bytes. Returns zero in non-debug mode
/// </summary>
/// <returns>Current usage</returns>
public static long GetCurrentMemoryUsage()
{
#if DEBUG
// don't use DeviceExtendedProperties for release builds (requires a capability)
return (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
#else
return 0;
#endif
}
/// <summary>
/// Gets the peak memory usage, in bytes. Returns zero in non-debug mode
/// </summary>
/// <returns>Peak memory usage</returns>
public static long GetPeakMemoryUsage()
{
#if DEBUG
// don't use DeviceExtendedProperties for release builds (requires a capability)
return (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
#else
return 0;
#endif
}
private static void ShowPopup()
{
popup = new Popup();
double fontSize = (double)Application.Current.Resources["PhoneFontSizeSmall"] - 2;
Brush foreground = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
StackPanel sp = new StackPanel { Orientation = Orientation.Horizontal, Background = (Brush)Application.Current.Resources["PhoneSemitransparentBrush"] };
currentMemoryBlock = new TextBlock { Text = "---", FontSize = fontSize, Foreground = foreground };
peakMemoryBlock = new TextBlock { Text = "", FontSize = fontSize, Foreground = foreground, Margin = new Thickness(5, 0, 0, 0) };
sp.Children.Add(currentMemoryBlock);
sp.Children.Add(new TextBlock { Text = " kb", FontSize = fontSize, Foreground = foreground });
sp.Children.Add(peakMemoryBlock);
sp.RenderTransform = new CompositeTransform { Rotation = 90, TranslateX = 480, TranslateY = 425, CenterX = 0, CenterY = 0 };
popup.Child = sp;
popup.IsOpen = true;
}
private static void StartTimer(TimeSpan timespan)
{
timer = new DispatcherTimer();
timer.Interval = timespan;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
static void timer_Tick(object sender, EventArgs e)
{
if (forceGc)
GC.Collect();
UpdateCurrentMemoryUsage();
UpdatePeakMemoryUsage();
}
private static void UpdatePeakMemoryUsage()
{
if (alreadyFailedPeak)
return;
long peak = GetPeakMemoryUsage();
if (peak >= MAX_MEMORY)
{
alreadyFailedPeak = true;
Checkpoint("*MEMORY USAGE FAIL*");
peakMemoryBlock.Text = "FAIL!";
peakMemoryBlock.Foreground = new SolidColorBrush(Colors.Red);
if (Debugger.IsAttached)
Debug.Assert(false, "Peak memory condition violated");
}
}
private static void UpdateCurrentMemoryUsage()
{
long mem = GetCurrentMemoryUsage();
currentMemoryBlock.Text = string.Format("{0:N}", mem / 1024);
int safetyBand = GetSafetyBand(mem);
if (safetyBand != lastSafetyBand)
{
currentMemoryBlock.Foreground = GetBrushForSafetyBand(safetyBand);
lastSafetyBand = safetyBand;
}
}
private static Brush GetBrushForSafetyBand(int safetyBand)
{
switch (safetyBand)
{
case 0:
return new SolidColorBrush(Colors.Green);
case 1:
return new SolidColorBrush(Colors.Orange);
default:
return new SolidColorBrush(Colors.Red);
}
}
private static int GetSafetyBand(long mem)
{
double percent = (double)mem / (double)MAX_MEMORY;
if (percent <= 0.75)
return 0;
if (percent <= 0.90)
return 1;
return 2;
}
private static void StopTimer()
{
timer.Stop();
timer = null;
}
private static void HidePopup()
{
popup.IsOpen = false;
popup = null;
}
}
/// <summary>
/// Holds checkpoint information for diagnosing memory usage
/// </summary>
public class MemoryCheckpoint
{
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="text">Text for the checkpoint</param>
/// <param name="memoryUsage">Memory usage at the time of the checkpoint</param>
internal MemoryCheckpoint(string text, long memoryUsage)
{
Text = text;
MemoryUsage = memoryUsage;
}
/// <summary>
/// The text associated with this checkpoint
/// </summary>
public string Text { get; private set; }
/// <summary>
/// The memory usage at the time of the checkpoint
/// </summary>
public long MemoryUsage { get; private set; }
}
}