.NET PerformanceCounter求得到錯誤的Memory數值
這篇是要討論如何使用.NET Framework來取得運行主機上面的記憶體狀態,看到這裡可能會有很多人第一印象就是,這還用講嗎?利用System.Diagnostics.PerformanceCounter來取得不是兩三行就寫完了嗎?這種想法也對也錯,如果你只是使用Console隨便POC一下,你可能會覺得數據貼近你在工作管理員(Task Manager)中看到的數據,因此就覺得這樣就是正確了。但是事實不然,如果你將這些程式碼移到Web端環境(例如ASP.NET),那你就會發現表現出來的數字遠遠與工作管理員背道而馳。
先示範一下常見的PerformanceCounter取得方式
using (System.Diagnostics.PerformanceCounter oRAM = new System.Diagnostics.PerformanceCounter("Memory", "% Committed Bytes in Use"))
{
//System.Diagnostics.PerformanceCounter oRAM = new System.Diagnostics.PerformanceCounter("Memory", "Available MBytes");
fRAM = oRAM.NextValue();
}
如果你細心一點的將這些Code轉到非Console本機運行的環境,例如使用Web端脫離Administrator最高權限的運行,你取得到的數據會變得非常的不正確,無論是「% Committed Bytes in Use」或是「Available MBytes」皆然。不信你自己部屬到正式網站後,遠端開啟Task Manager,一面去刷新該台WebPage,你就會看到你取得到的這些數字根本是笑話。
那要如何取用到正確的系統記憶體狀態
沒錯,只能回到Windows API來取得,Windows API才是王道。下面是我建立的一個類別,該類別會傳回極度貼近工作管理員的記憶體數據。
public class PerformanceInfo
{
[System.Runtime.InteropServices.DllImport("psapi.dll", SetLastError = true)]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
public static extern bool GetPerformanceInfo([System.Runtime.InteropServices.Out] out PerformanceInformation PerformanceInformation, [System.Runtime.InteropServices.In] int Size);
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct PerformanceInformation
{
public int Size;
public System.IntPtr CommitTotal;
public System.IntPtr CommitLimit;
public System.IntPtr CommitPeak;
public System.IntPtr PhysicalTotal;
public System.IntPtr PhysicalAvailable;
public System.IntPtr SystemCache;
public System.IntPtr KernelTotal;
public System.IntPtr KernelPaged;
public System.IntPtr KernelNonPaged;
public System.IntPtr PageSize;
public int HandlesCount;
public int ProcessCount;
public int ThreadCount;
}
//Item1:記憶體總數MB、Item2:剩餘多少可用記憶體MB
public static System.Tuple<decimal, decimal> GetRamState()
{
PerformanceInformation oPI = new PerformanceInformation();
if (GetPerformanceInfo(out oPI, System.Runtime.InteropServices.Marshal.SizeOf(oPI)))
{
return new System.Tuple<decimal, decimal>
(
System.Convert.ToDecimal((oPI.PhysicalTotal.ToInt64() * oPI.PageSize.ToInt64() / 1048576)),
System.Convert.ToDecimal((oPI.PhysicalAvailable.ToInt64() * oPI.PageSize.ToInt64() / 1048576))
);
}
else
{ return new System.Tuple<decimal, decimal>(0, 0); }
}
}
調用方式很簡單,請看下列程式碼:
static void Main(string[] args)
{
System.Tuple<decimal, decimal> oRAM = PerformanceInfo.GetRamState();
WriteLine(string.Format(
"總記憶體數:{0}\n可用記憶體數:{1}\n使用中記憶體數:{2}\n記憶體佔用率:{3}%",
oRAM.Item1,
oRAM.Item2,
oRAM.Item1 - oRAM.Item2,
string.Format("{0:0%}", 1 - (oRAM.Item2 / oRAM.Item1))
));
Read();
}