一:背景

前几篇我们聊的都是 非托管内存泄漏,这一篇我们再看下如何用 PerfView 来排查 托管内存泄漏 ,其实 托管内存泄漏 比较好排查,尤其是用 WinDbg,毕竟C#是带有丰富的元数据,不像C++下去就是二进制。

二:如何分析

PerfView 用的是权重占比来寻找可疑的问题函数,为了方便讲述,我们先上一段问题代码。


internal class Program
{
static void Main(string[] args)
{
Task.Run(Alloc1);
Task.Run(Alloc2);
Task.Run(Alloc3); Console.ReadLine();
} static void Alloc1()
{
var list = new List<string>(); for (int i = 0; i < 200000; i++)
{
list.Add(string.Join(",", Enumerable.Range(0, 1000)));
} Console.WriteLine("Alloc1 处理完毕");
} static void Alloc2()
{
var list = new List<string>(); for (int i = 0; i < 100; i++)
{
list.Add(string.Join(",", Enumerable.Range(0, 1000)));
} Console.WriteLine("Alloc2 处理完毕");
} static void Alloc3()
{
var list = new List<string>(); for (int i = 0; i < 100; i++)
{
list.Add(string.Join(",", Enumerable.Range(0, 1000)));
} Console.WriteLine("Alloc3 处理完毕");
}
}

这段代码运行完成后会发现内存占用高达 1.5G,如下图所示:

在真实场景中,你根本不知道是谁占用了这么大的内存,在分析武器库中,用 WinDbg 肯定是最稳的,既然是介绍 PerfView 工具,得用它来分析。

二:PerfView 分析

1. 到底是哪里的泄漏

分析之前,还是要先搞清楚到底是哪里的泄漏,才好用 PerfView 追查下来,首先用 !eeheap -gc 查看下托管堆的占用大小。


0:005> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000000072D7AEC0
generation 1 starts at 0x0000000072B1B790
generation 2 starts at 0x0000000002841000
ephemeral segment allocation context: none
segment begin allocated committed allocated size committed size
0000000002840000 0000000002841000 000000001283FB10 0000000012840000 0xfffeb10(268430096) 0xffff000(268431360)
0000000023E80000 0000000023E81000 0000000033E7F0A8 0000000033E80000 0xfffe0a8(268427432) 0xffff000(268431360)
00000000347D0000 00000000347D1000 00000000447CFA98 00000000447D0000 0xfffea98(268429976) 0xffff000(268431360)
0000000045A60000 0000000045A61000 0000000055A5E2A0 0000000055A60000 0xfffd2a0(268423840) 0xffff000(268431360)
0000000055A60000 0000000055A61000 0000000065A5F7B8 0000000065A60000 0xfffe7b8(268429240) 0xffff000(268431360)
0000000065A60000 0000000065A61000 0000000073252ED8 00000000735F6000 0xd7f1ed8(226434776) 0xdb95000(230248448)
Large object heap starts at 0x0000000012841000
segment begin allocated committed allocated size committed size
0000000012840000 0000000012841000 0000000012C21130 0000000012C22000 0x3e0130(4063536) 0x3e1000(4067328)
Pinned object heap starts at 0x000000001A841000
000000001A840000 000000001A841000 000000001A845C38 000000001A852000 0x4c38(19512) 0x11000(69632)
Total Allocated Size: Size: 0x5dbcdce8 (1572658408) bytes.
Total Committed Size: Size: 0x5df71000 (1576472576) bytes.
------------------------------
GC Allocated Heap Size: Size: 0x5dbcdce8 (1572658408) bytes.
GC Committed Heap Size: Size: 0x5df71000 (1576472576) bytes.

从输出中可以看到,当前的 托管堆 占用 1.5G, 这就说明当前的泄漏确实是 托管堆 的泄漏,这就给继续分析指明了方向。

2. 使用 .NET Alloc 拦截

在 PerfView 中有一个 .NET Alloc 选项,它可以拦截每一次对象分配,然后记录下 线程调用栈,再根据分配量计算权重,知道原理后,接下来就可以开启 .NET Alloc 拦截。

需要注意的是,对于这个选项,需要先开启收集,再启动程序,等程序执行完毕后,点击 Stop Collection ,稍等片刻,会看到如下截图。

点击 GC Heap Net MEM (Coarse Sampling) Stack 列表,选择我们的进程,会看到当前的 System.String 权重占比最高,所以调查它的分配源就是当务之急了,截图如下:

接下来双击 System.String 行,查看它的 Callers,逐一往下翻,终于找到了 Program.Alloc1() 方法,截图如下:

到这里就找到了问题函数 Alloc1() ,接下来就是探究源码了哈。

3. 生产中可以用 .NET Alloc 吗

现在大家都知道 .NET Alloc 可以实现对象分配拦截,但是在生产场景中,每秒的分配量可能达到几十万,上百万,每一次分配都要拦截,会产生诸多的负面影响。

1) 程序速度变慢。

2) 产生非常大的 zip 文件。

如果你不在意的话,可以这么使用,如果在意,建议用 .NET SampAlloc 选项,它是一种采样的方式,每秒中的同类型分配最多只会采样 100 次,所以在 性能zip文件 两个维度可以达到最优状态。

接下来勾选 .NET SampAlloc 项,其他操作步骤一致,截图如下:

有点意思的是,观察到的占比都是 43.7% ,哈!

最新文章

  1. C#File类常用的文件操作方法(创建、移动、删除、复制等)
  2. Java数据结构——栈
  3. poj 3694 pku 3694 Network tarjan求割边 lca
  4. Slony-I的删除:
  5. JavaScript DOM编程基础精华01(DOM入门,DOM模型和获取页面元素,事件,window对象的方法)
  6. RabbitMQ 入门 Helloworld -摘自网络
  7. 17.1.2 Replication Formats
  8. 为什么选择Premium Fabric Shanghai
  9. bootstrap读书笔记
  10. Android 3d云标签
  11. POJ1113 Wall 凸包
  12. EF开发程序经常用的几个东西
  13. 小结:Swift、OC语言中多target在代码中如何区分
  14. C#在截屏时将截屏之前需要隐藏的控件也截入
  15. 表单、框架结构的大概、CSS开头(选择器以及常用属性)
  16. SAP 费用
  17. AJAX简单介绍
  18. Zhu and 772002---hdu5833(高斯消元解求异或方程组)
  19. javassist示例
  20. angular2自学笔记(三)---ng2选项卡

热门文章

  1. Johnson 全源最短路
  2. Python中plt.plot()、plt.scatter()和plt.legend函数的用法示例
  3. @vue/cli3+配置build命令构建测试包&amp;正式包
  4. python创建分类器小结
  5. SpringBoot + JWT + Redis 开源知识社区系统
  6. FICO 常用事务码
  7. 【react】什么是fiber?fiber解决了什么问题?从源码角度深入了解fiber运行机制与diff执行
  8. UiPath循环活动For Each的介绍和使用
  9. jieba分词的功能和性能分析
  10. adb工具