簡易版記憶體用量觀察工具

找資料時發現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();
    }
}

補充:

  1. GC.GetTotalMemory()只能取得Managed Heap部分的大小,若變數耗用的是Stack部分的記憶體(例如: Value Type),則不在量測範圍內。
  2. GetTotalMemeory()只能量測總耗用量,無法精準指出特定變數/物件所耗用記憶體大小。
  3. MemWatch預設會包含配置後棄用但尚未被GC機制回收的記憶體量,若要強制回收可自行呼叫GC.Collect()或用MemWatch(true)建構式啟用強制回收模式。注意,這裡是因測試需要才自行操作GC回收,實務上應避免在程式中自行呼叫GC.Collect(),最好交由.NET Runtime自行決定何時進行回收,較有利於整體效能。
  4. Compiler常會針對程式碼進行最佳化(例如: 忽略只宣告但完全未使用的物件、改變程式執行順序以改善效率...等等),因此當執行結果與預期出入很大時,可研究看看是否與最佳化有關。
歡迎推文分享:
Published 17 February 2010 01:20 PM 由 Jeffrey
Filed under:
Views: 51,671



意見

# licicous0421 said on 11 August, 2016 11:31 PM

最近剛好有遇到記憶體釋放的問題!

想請問一下~

測試3

最後面

//MemWatch測量時會先將b2釋出的記憶體回收,故結果為0KB

可是b3生成的空間不是還未被回收嗎?

這樣測出來因該是256KB??

# Jeffrey said on 12 August, 2016 01:25 AM

to licicous0421, 測試3在mw.Stop()前有b3=null,而在Stop()中MemorySizeChange =             GC.GetTotalMemory(_forceGC) - _lastTotalMemory;的_forceGC==true,會先回收b3使用記憶體再測量記憶體用量,故結果為0KB。

# Jason said on 11 October, 2016 01:41 AM

我以前有遇到一個問題是,我試過做一個視窗,裡面沒功能, 只是放著沒做什麼事 在工作管理員中每隔一段時間就會看到記憶體使用量增加了一些 慢慢的增加,記憶體越用越多 這是為什麼?

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<February 2010>
SunMonTueWedThuFriSat
31123456
78910111213
14151617181920
21222324252627
28123456
78910111213
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication