一:背景

前几篇我们聊的都是 非托管内存泄漏,这一篇我们再看下如何用 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. 优秀开源平台,前后端分离快速开发平台,一站式多端开发(PC+APP)

    JNPF平台架构介绍 JNPF快速开发平台采用前后端分离技术.采用B/S架构开发,形成一站式开发多端(APP+PC)使用. PC端版本介绍 第一个当然是当下热门的.net core了,运行环境为Vis ...

  2. Java命令行传递参数

    目录 命令行传参 代码运行 视频 命令行传参 有时候你希望运行一个程序的时候再传递给它消息. 这要靠传递命令行参数给main()函数实现 package com.broky.base; public ...

  3. 【clickhouse专栏】单机版的安装与验证

    <clickhouse专栏>第三节内容,先安装一个单机版的clickhouse,是后续学习多副本或者分布式集群安装的基础内容.但基本的clickhouse是不依赖于zookeeper的,只 ...

  4. CabloyJS自带工作流引擎的文档清单

    文档清单 CabloyJS自带工作流引擎文档已经整理出来,欢迎大家围观.拍砖 介绍 介绍 演示:CMS审批工作流 单元测试用例集 流程定义 基本概念 JSON规范 listener规范 listene ...

  5. Java 基础常见知识点&面试题总结(上),2022 最新版!| JavaGuide

    你好,我是 Guide.秋招即将到来,我对 JavaGuide 的内容进行了重构完善,公众号同步一下最新更新,希望能够帮助你. 基础概念与常识 Java 语言有哪些特点? 简单易学: 面向对象(封装, ...

  6. docker-compose 搭建 Prometheus+Grafana监控系统

    有关监控选型之前有写过一篇文章: 监控系统选型,一文轻松搞定! 监控对象 Linux服务器 Docker Redis MySQL 数据采集 1).prometheus: 采集数据 2).node-ex ...

  7. vue2升级vue3:vue2 vue-i18n 升级到vue3搭配VueI18n v9

    项目从vue2 升级vue3,VueI18n需要做适当的调整.主要是Vue I18n v8.x 到Vue I18n v9 or later 的变化,其中初始化: 具体可以参看:https://vue- ...

  8. 【微服务专题之】.Net6下集成消息队列上-RabbitMQ

    ​ 微信公众号:趣编程ACE关注可了解更多的.NET日常实战开发技巧,如需源码 请公众号后台留言 源码;[如果觉得本公众号对您有帮助,欢迎关注] .Net中RabbitMQ的使用 [微服务专题之].N ...

  9. 《深入理解java虚拟机》读书笔记-第二章Java内存区域和内存溢出异常

    java1.7和java8的jvm存在差异,本文先按照<深入理解java虚拟机>的讲解内容总结,并将java8的改变作为附录放在文末 一丶运行时数据区域 ​ 图:java虚拟机运行时数据区 ...

  10. IIS部署的H5的单页面跳转的配置

    <?xml version="1.0" encoding="UTF-8"?><configuration> <system.web ...