【文章标题】: 剖面分析和性能计数器的研究
【文章作者】: 有酒醉
【作者邮箱】:
[email protected]
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
一、前言
题目为研究,事实上只是皮毛上的理解,还待于以后在深入应用中慢慢体会它的乐趣吧
二、理解性能监视的一些概念以及注意点
1、性能监视器
使用性能计数器的实用程序被称为性能监视器,它和profiling API结合起来就构成了剖析工具(profiler)
2、通常性能监视器是由两种方式实现的:
2.1、性能计数器派生
性能计数器可以被认为是一种连续的记录信息的组件,将存储它们从内存映射文件中得到的信息
2.2、profiling API
profiling API 是由CLR提供的一个工具,外部非托管COM组件可以在托管程序进行JIT编译或执行时,通过它请求接收所发生事件的通知.典型的事件有:方法调
用,类实例化以及异常的抛出和捕获.
3、尽可能采用发布版本进行剖析
调试版本包含大量额外的代码,它们占用内存,长时间占用资源,并改变不同方法的相对执行时间,无法准确地反映发布版本的执行情况
4、代码中把虚拟内存映射到物理内存页面叫做提交,而内存访问违例通常是由于可执行代码试图访问没有提交的虚拟地址所造成的.
5、页中断
如果应用程序要引用已经转移到磁盘中的页面,系统将在它们被访问前自动地把页面换回到RAM中,一旦发生这种情况,我们就称应用程序引起了一个页中断.对
于性能来说,最重要的当然是页中断所需的时间.避免页中断的主要方法是尽可能地减少应用程序所需的虚拟内存.
6、任务管理器的VM Size 列包含了共享内存
三、通过一段代码来测试任务管理器
源代码 - 虚拟内存及工作集测试
// T.cs
// author by Yzl
using System;
using System.Diagnostics;
using System.Collections;
using System.Runtime.InteropServices;
public class T
{
public static void Main(string[] args)
{
string strProp = "/n分配1MB的内存:/t0/n分配50MB的内存:/t1/n" +
"重新整理工作集:/t2/n清空已分配内存:/t3/n退出:/t/t4/n";
string strRead = null;
ArrayList arrays = new ArrayList();
Console.WriteLine("------------性能监视器测试-任务管理器--------------");
Console.WriteLine(strProp);
strRead = Console.ReadLine();
while(!strRead.Equals("4"))
{
switch(strRead)
{
case "0": Allocate1MB(arrays); break;
case "1": Allocalte50MB(arrays); break;
case "2": EmptyWorkingSet(Process.GetCurrentProcess().Handle);break;
case "3": CleanupArray(arrays); break;
}
Console.WriteLine(strProp);
strRead = Console.ReadLine();
}
Console.WriteLine("正在退出系统.../n已退出,感谢您的使用!");
Console.WriteLine("------------性能监视器测试-任务管理器--------------");
}
private static void Allocate1MB(ArrayList arrays)
{
arrays.Add(new MegaByteClass());
Console.WriteLine("操作结果:/t"+arrays.Count.ToString()+"M内存已分配./n");
}
private static void Allocalte50MB(ArrayList arrays)
{
for (int i = 0; i < 50; i ++)
arrays.Add(new MegaByteClass());
Console.WriteLine("操作结果:/t"+arrays.Count.ToString()+"M内存已分配./n");
}
private static void CleanupArray(ArrayList arrays)
{
arrays.Clear();
Console.WriteLine("操作结果:/t分配内存已被清空./n");
GC.Collect(GC.MaxGeneration);
}
[DllImport("psapi.dll")]
static extern int EmptyWorkingSet(IntPtr hProcess);
}
public class MegaByteClass
{
int[][] array = new int[100][];
public MegaByteClass()
{
for (int i = 0; i < 100; i ++)
array[i] = new int[2600];
}
}
运行观察任务管理器我们可以发现,当虚拟内存增加到一定程度,工作集并不会相应地增加那么多.这是什么原因呢?原来Windows认为工作集过于庞大,所以
进行了页面换出.
四、性能计数器
任务管理器和PerfMon性能监视程序输出从性能计数器中读取的数据,我们也可以通过编程的方式获取性能计数器.PerfMon的使用方式这里就不讲了,有兴趣
的朋友可以自己研究研究.下面通过一个例子来讲解一下如何用编程的方式获取性能方面的信息
源代码 - 无用单元收集generation 0/1 的次数
// T.cs
// author by Yzl
using System;
using System.Diagnostics;
using System.Collections;
using System.Runtime.InteropServices;
public class T
{
public static void Main(string[] args)
{
string strProp = "/n分配1MB的内存:/t/t0/n分配50MB的内存:/t/t1/n" +
"查看无用单元收集:/t2/n清空已分配内存:/t/t3/n重新整理工作集:/t/t4/n"+"退出:/t/t/t5/n";
string strRead = null;
ArrayList arrays = new ArrayList();
Console.WriteLine("------------性能监视器测试-无用单元收集--------------");
Console.WriteLine(strProp);
strRead = Console.ReadLine();
while(!strRead.Equals("5"))
{
switch(strRead)
{
case "0": Allocate1MB(arrays); break;
case "1": Allocalte50MB(arrays); break;
case "4": EmptyWorkingSet(Process.GetCurrentProcess().Handle);break;
case "3": CleanupArray(arrays); break;
// Add
case "2": DisplayGenCollections();break;
}
Console.WriteLine(strProp);
strRead = Console.ReadLine();
}
Console.WriteLine("正在退出系统.../n已退出,感谢您的使用!");
Console.WriteLine("------------性能监视器测试-无用单元收集--------------");
}
private static void Allocate1MB(ArrayList arrays)
{
arrays.Add(new MegaByteClass());
Console.WriteLine("操作结果:/t"+arrays.Count.ToString()+"M内存已分配./n");
}
private static void Allocalte50MB(ArrayList arrays)
{
for (int i = 0; i < 50; i ++)
arrays.Add(new MegaByteClass());
Console.WriteLine("操作结果:/t"+arrays.Count.ToString()+"M内存已分配./n");
}
private static void CleanupArray(ArrayList arrays)
{
arrays.Clear();
Console.WriteLine("操作结果:/t分配内存已被清空./n");
GC.Collect(GC.MaxGeneration);
}
// Add
private static void DisplayGenCollections()
{
// 获取性能计数器
PerformanceCounter pcGen0Collections = new PerformanceCounter(".NET CLR Memory","# Gen 0 Collections","T");
PerformanceCounter pcGen1Collections = new PerformanceCounter(".NET CLR Memory","# Gen 1 Collections","T");
// 初始化性能计数器
pcGen0Collections.BeginInit();
pcGen1Collections.BeginInit();
Console.WriteLine("#Gen 0 Collections:/t"+pcGen0Collections.NextValue().ToString());
Console.WriteLine("#Gen 1 Collections:/t"+pcGen1Collections.NextValue().ToString());
Console.WriteLine();
}
[DllImport("psapi.dll")]
static extern int EmptyWorkingSet(IntPtr hProcess);
}
public class MegaByteClass
{
int[][] array = new int[100][];
public MegaByteClass()
{
for (int i = 0; i < 100; i ++)
array[i] = new int[2600];
}
}
这个程序跟第一个程序没多大区别,只是多了1个函数.通过测试我们可以发现,随着分配更多的数组字节,无用单元收集逐渐累积.在分配的空间达到一定的数
量之前,generation 1 收集不会频繁发生 -- 此时,无用单元收集器会认真地监听能够回收的内存.关于无用单元收集器我不细讲,有兴趣的可以找找资料.
五、注册自己的性能计数器
源代码
// T.cs
// author by Yzl
using System;
using System.Timers;
using System.Collections;
using System.Diagnostics;
public class T
{
private Timer timer = new Timer(1000);
private ArrayList arrays = new ArrayList();
private PerformanceCounter pcGen0Collections = null;
private PerformanceCounter pcCommittedBytes = null;
private PerformanceCounter pcMegaByteAllocs = null;
private string strCategoryName = null;
private string strCategoryHelp = null;
private string strCounterName = null;
private string strCounterHelp = null;
private string strInstanceName = null;
public T()
{
strInstanceName = "T";
strCounterHelp = "Counts total MegaByteClass allocations";
strCounterName = "# MegaByteClasses created";
strCategoryHelp = "Custom Performace Sample";
strCategoryName = "Yzl";
timer.Elapsed+=new ElapsedEventHandler(OnTimedEvent);
timer.Enabled = false;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
Allocate1MB();
Console.WriteLine("#Gen 0 Collections:/t/t"+pcGen0Collections.NextValue().ToString());
Console.WriteLine("# Total committed Bytes:/t"+pcCommittedBytes.NextValue().ToString());
Console.WriteLine("# MegaByteClasses created:/t"+pcMegaByteAllocs.RawValue.ToString());
}
public void RegisterCustomCounter()
{
pcGen0Collections = new PerformanceCounter(".NET CLR Memory","# Gen 0 Collections","T");
pcCommittedBytes = new PerformanceCounter(".NET CLR Memory","# Total committed Bytes","T");
// 注册包含 NumberOfItems32 类型的单个计数器的自定义性能计数器类别
if (!PerformanceCounterCategory.Exists(strCategoryName))
{
PerformanceCounterCategory.Create(strCategoryName,strCategoryHelp,strCounterName,strCounterHelp);
}
pcMegaByteAllocs = new PerformanceCounter(strCategoryName,strCounterName,strInstanceName,false);
pcMegaByteAllocs.RawValue = 0;
// 初始化
pcGen0Collections.BeginInit();
pcCommittedBytes.BeginInit();
pcMegaByteAllocs.BeginInit();
}
public void DeregisterCustomCounter()
{
if (PerformanceCounterCategory.Exists(strCategoryName))
{
PerformanceCounterCategory.Delete(strCategoryName);
}
arrays.Clear();
GC.Collect();
}
private void Allocate1MB()
{
arrays.Add(new MegaByteClass());
Console.WriteLine("操作结果:/t"+arrays.Count.ToString()+"M内存已分配./n");
pcMegaByteAllocs.RawValue += 1;
}
public void Start()
{
timer.Start();
}
public void Stop()
{
timer.Stop();
}
public static void Main(string[] args)
{
T t = new T();
t.Start();
t.RegisterCustomCounter();
while(Console.Read()!='q');
t.DeregisterCustomCounter();
}
}
public class MegaByteClass
{
int[][] array = new int[100][];
public MegaByteClass()
{
for (int i = 0; i < 100; i ++)
array[i] = new int[2600];
}
}
运行结果如下;
D:/>T.exe
操作结果: 1M内存已分配.
#Gen 0 Collections: 2
# Total committed Bytes: 1294336
# MegaByteClasses created: 1
操作结果: 2M内存已分配.
#Gen 0 Collections: 2
# Total committed Bytes: 1294336
# MegaByteClasses created: 2
操作结果: 3M内存已分配.
#Gen 0 Collections: 2
# Total committed Bytes: 1294336
# MegaByteClasses created: 3
注册自己的性能计数器过程:
1、创建性能计数器类别
2、创建性能计数器并关联实例
3、根据具体需要更改性能计数器的属性
4、当不在使用时,可以删除性能计数器
提示:通过PerfMon你可以查看到自定义性能计数器类别"Yzl".
End.
--------------------------------------------------------------------------------
【版权声明】: 本文原创于泉州软件基地, 转载请注明作者并保持文章的完整, 谢谢!