在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;
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
; }
}
}