簡易版記憶體用量觀察工具
3 |
找資料時發現GC.GetTotalMemory()這個方法,看到有些人用它來測量記憶體使用狀況,我也好奇玩了一下,包成一個MemWatch Class(比照Stopwatch的概念):
//簡易版的記憶體用量觀察工具
// 透過觀察Managed Heap的總使用量變化
// 粗略推估某段程式所耗用的記憶體大小
class MemWatch
{
//比較記憶體使用量變化的基準值
private long _lastTotalMemory = 0;
//記憶體使用量變化
public long MemorySizeChange = 0;
//是否強制GC再測量記憶體用量
private bool _forceGC = false;
//可指定測量前是否要先做GC
//(可排除己不用但尚未回收的記憶體)
public MemWatch(bool forceGC)
{
_forceGC = forceGC;
}
public MemWatch() : this(false) { }
//保留測量開始之基準
public void Start()
{
_lastTotalMemory =
GC.GetTotalMemory(_forceGC);
}
//測量從Start()至今的記憶體變化
public void Stop()
{
MemorySizeChange =
GC.GetTotalMemory(_forceGC) - _lastTotalMemory;
}
//記憶體使用量變化(以KB計)
public string MemorySizeChangeInKB
{
get
{
return string.Format("{0:N0}KB",
MemorySizeChange / 1024);
}
}
//記憶體使用量變化(以MB計)
public string MemorySizeChangeInMB
{
get
{
return string.Format("{0:N0}MB",
MemorySizeChange / 1024 / 1024);
}
}
}
測試時,故意宣告指定大小的byte[],看看結果是否如預期:
class Program
{
static void Main(string[] args)
{
//測試1-宣告byte陣列記憶體使用
//利用MemWatch觀察Heap記憶體使用狀況
MemWatch mw = new MemWatch();
//宣告前先記錄使用前的記憶體用量
mw.Start();
byte[] b1 = new byte[512 * 1024];
//宣告後再測一次
mw.Stop();
//會得到二次的記體變化差距為512KB
Console.WriteLine(mw.MemorySizeChangeInKB);
//測試2-這次宣告byte陣列後立即棄用,但不做GC
mw.Start();
byte[] b2 = new byte[256 * 1024];
b2 = null;
mw.Stop();
//b2佔用記憶體已釋出,但尚未回收,結果為256KB
Console.WriteLine(mw.MemorySizeChangeInKB);
//進行下一階段測試前先強制GC
GC.Collect();
//測試3-同2,但換成先GC才測記憶體大小的測量模式
mw = new MemWatch(true);
mw.Start();
byte[] b3 = new byte[256 * 1024];
b3 = null;
mw.Stop();
//MemWatch測量時會先將b2釋出的記憶體回收,故結果為0KB
Console.WriteLine(mw.MemorySizeChangeInKB);
Console.Read();
}
}
補充:
- GC.GetTotalMemory()只能取得Managed Heap部分的大小,若變數耗用的是Stack部分的記憶體(例如: Value Type),則不在量測範圍內。
- GetTotalMemeory()只能量測總耗用量,無法精準指出特定變數/物件所耗用記憶體大小。
- MemWatch預設會包含配置後棄用但尚未被GC機制回收的記憶體量,若要強制回收可自行呼叫GC.Collect()或用MemWatch(true)建構式啟用強制回收模式。注意,這裡是因測試需要才自行操作GC回收,實務上應避免在程式中自行呼叫GC.Collect(),最好交由.NET Runtime自行決定何時進行回收,較有利於整體效能。
- Compiler常會針對程式碼進行最佳化(例如: 忽略只宣告但完全未使用的物件、改變程式執行順序以改善效率...等等),因此當執行結果與預期出入很大時,可研究看看是否與最佳化有關。
Comments
# by licicous0421
最近剛好有遇到記憶體釋放的問題! 想請問一下~ 測試3 最後面 //MemWatch測量時會先將b2釋出的記憶體回收,故結果為0KB 可是b3生成的空間不是還未被回收嗎? 這樣測出來因該是256KB??
# by Jeffrey
to licicous0421, 測試3在mw.Stop()前有b3=null,而在Stop()中MemorySizeChange = GC.GetTotalMemory(_forceGC) - _lastTotalMemory;的_forceGC==true,會先回收b3使用記憶體再測量記憶體用量,故結果為0KB。
# by Jason
我以前有遇到一個問題是,我試過做一個視窗,裡面沒功能, 只是放著沒做什麼事 在工作管理員中每隔一段時間就會看到記憶體使用量增加了一些 慢慢的增加,記憶體越用越多 這是為什麼?