一:背景

前几篇我们聊的都是 非托管内存泄漏,这一篇我们再看下如何用 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% ,哈!

PerfView专题 (第五篇):如何寻找 C# 托管内存泄漏的更多相关文章

  1. PerfView专题 (第四篇):如何寻找 C# 中程序集泄漏

    一:背景 前两篇我们都聊到了非托管内存泄漏,一个是 HeapAlloc ,一个是 VirtualAlloc,除了这两种泄漏之外还存在其他渠道的内存泄漏,比如程序集泄漏,这一篇我们就来聊一聊. 二: 程 ...

  2. PerfView专题 (第三篇):如何寻找 C# 中的 VirtualAlloc 内存泄漏

    一:背景 上一篇我们聊到了如何用 PerfView 去侦察 NTHeap 的内存泄漏,这种内存泄漏往往是用 C 的 malloc 或者 C++ 的 new 分配而不释放所造成的,这一篇我们来聊一下由 ...

  3. PerfView专题 (第八篇):洞察 C# 内存泄漏之寻找静态变量名和GC模式

    一:背景 这篇我们来聊一下 PerfView 在协助 WinDbg 分析 Dump 过程中的两个超实用技巧,可能会帮助我们快速定位最后的问题,主要有如下两块: 洞察内存泄漏中的静态大集合变量名. 验证 ...

  4. PerfView专题 (第十篇):洞察 C# 终结队列引发的内存泄漏

    一:背景 C# 程序内存泄漏的诱发因素有很多,但从顶层原理上来说,就是该销毁的 用户根 对象没有被销毁,从而导致内存中意料之外的对象无限堆积,导致内存暴涨,最终崩溃,这其中的一个用户根就是 终结器队列 ...

  5. PerfView专题 (第十一篇):使用 Diff 功能洞察 C# 内存泄漏增量

    一:背景 去年 GC架构师 Maoni 在 (2021 .NET 开发者大会) [https://ke.segmentfault.com/course/1650000041122988/section ...

  6. PerfView专题 (第六篇):如何洞察 C# 中 GC 的变化

    一:背景 在洞察 GC 方面,我觉得市面上没有任何一款工具可以和 PerfView 相提并论,这也是为什么我会在 WinDbg 之外还要学习这么一款工具的原因,这篇我们先简单聊聊 PerfView 到 ...

  7. PerfView专题 (第七篇):如何洞察触发 GC 的 C# 代码?

    一:背景 上一篇我们聊到了如何用 PerfView 洞察 GC 的变化,但总感觉还缺了点什么? 对,就是要跟踪到底是什么代码触发了 GC,这对我们分析由于 GC 导致的 CPU 爆高有非常大的参考价值 ...

  8. Java提高篇—— 简单介绍Java 的内存泄漏

    java最明显的一个优势就是它的内存管理机制.你只需简单创建对象,java的垃圾回收机制负责分配和释放内存.然而情况并不像想像的那么简单,因为在Java应用中经常发生内存泄漏. 本教程演示了什么是内存 ...

  9. Android 性能篇 -- 带你领略Android内存泄漏的前世今生

    基础了解 什么是内存泄漏? 内存泄漏是当程序不再使用到的内存时,释放内存失败而产生了无用的内存消耗.内存泄漏并不是指物理上的内存消失,这里的内存泄漏是指由程序分配的内存但是由于程序逻辑错误而导致程序失 ...

随机推荐

  1. 记一次IIS网站启动不了的问题排查

    今天清理了下机器中的IIS网站,将很久不用的网站都删除. 因为需要删除的比较多,正在使用的很少,就将网站全部删除了,然后准备重新添加需要用的. 在添加了网站后,点击启动按钮,发现网站启动不了,因为网站 ...

  2. Python工程:ImportError: attempted relative import with no known parent package

    Python工程:ImportError: attempted relative import with no known parent package 解决方法: 1.对每个目录创建的时候都选择创建 ...

  3. Citrix Virtual Apps and Desktops 7 2203 LTSR虚拟云桌面单机教程

    哈喽大家好,欢迎来到虚拟化时代君(XNHCYL). 大家好,我是虚拟化时代君,一位潜心于互联网的技术宅男.这里每天为你分享各种你感兴趣的技术.教程.软件.资源.福利--(每天更新不间断) 一 .主要内 ...

  4. Simple, Fast Malicious Multiparty Private Set Intersection-解读

    文本记录阅读该论文的笔记. 这是文章框架,来自视频. 介绍 本文主要解决恶意攻击下安全的多方PSI,主要用到两大技术OPPRF和OKVS,构造合谋和不合谋的协议. 基础知识 OPPRF 这部分在OPR ...

  5. MySQL数据库3

    内容概要 自增特性 约束条件之外键 外键简介 外键关系 外键SQL语句之一对多关系 外键SQL语句之多对多关系 外键SQL语句之一对一关系 查询关键字 数据准备 查询关键字之select与from 查 ...

  6. electron vue

    vue create project vue add vue-cli-plugin-electron-builder node_modules\@vue\cli-service\lib\config\ ...

  7. 浅析Kubernetes架构之workqueue

    通用队列 在kubernetes中,使用go的channel无法满足kubernetes的应用场景,如延迟.限速等:在kubernetes中存在三种队列通用队列 common queue ,延迟队列 ...

  8. 在公网服务器搭建CobaltStrike4.0

    因为工作需要使用cs,正好之前腾讯云薅了一把羊毛,就把VPS装起来cs. 选的环境是centos7.6 cs运行需要java环境 先使用yum -y list java* 查看yum存在的java库 ...

  9. 自己封装的tools.js文件

    /* * 生成指定范围的随机整数 * @param lower 下限 * @param upper 上限 * @return 返回指定范围的随机整数,上/下限值均可取 */ function rand ...

  10. windows server2012R2 上 .net core IIS 部署--应用程序池 自动停止

    在windows server2016安装部署.NET CORE时,只需要将.net core应用程序池设置无托管,然后对应你项目的版本安装一个dotnet-hosting-2.2.6-win.exe ...