PerfView专题 (第八篇):洞察 C# 内存泄漏之寻找静态变量名和GC模式
一:背景
这篇我们来聊一下 PerfView 在协助 WinDbg 分析 Dump 过程中的两个超实用技巧,可能会帮助我们快速定位最后的问题,主要有如下两块:
洞察内存泄漏中的静态大集合变量名。
验证当前程序的 GC 模式。
这里就把经验分享一下,希望让大家少走弯路。
二:如何洞察
1. 查看静态变量名
如果有过 dump 分析经验的朋友应该知道,当你历经千辛万苦在 内存泄漏 的dump文件中找到了那个内存泄漏最大的集合,但遗憾的是,你不知道这个 集合 的变量名叫什么?
为了方便讲述,先上一段测试代码:
namespace ConsoleApp10
{
internal class Program
{
static void Main(string[] args)
{
Task.Run(Alloc1);
Console.ReadLine();
}
public static List<string> mybiglist = new List<string>();
static void Alloc1()
{
var rand = new Random();
for (int i = 0; i < 10000; i++)
{
mybiglist.Add(string.Join(",", Enumerable.Range(1, 1000)));
Console.WriteLine(mybiglist.Count);
}
}
}
}
接下来把程序跑起来,终于你找到了那个内存占用最大的 List<string> 集合,代码如下:
0:000> !gcroot -all 0000000002e27038
HandleTable:
00000000004A13E8 (strong handle)
-> 000000001A841018 System.Object[]
-> 000000000284D680 System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]]
-> 0000000012841038 System.String[]
-> 0000000002E27038 System.String
可以看到,这个变量被 HandleTable 所持有,从经验上来说其实就是一个 static 变量,现在我们迫切需要知道这个变量名叫什么,因为离真相真的咫尺之遥了。。。
如果你没有汇编基础,我敢打赌你肯定在 WinDBG 中找不到这个变量名。 那有没有快捷的方式显示变量名呢? 肯定是可以的,这就需要借助 PerfView 。
接下来点击菜单的 Memory -> Take Heap Snapshot From Dump 按钮,弹出如下对话框,输入 dump 文件以及 output 地址,截图如下:

接下来点击 Dump GC Heap 让 PerfView 从 ConsoleApp10.dmp 中采样生成 *.gcdump 文件,接下来点击 Heap Stacks -> RefTree ,通过 Inc% 可以观察到 [static vars] 下的 mybiglist 采样占比最大,如图所示:

到这里第一个问题也就解决了,原来是一个叫 mybiglist 的 List<string> 集合把内存给吃掉了,是不是非常的方便哈。
2. 查看手工修改的 GC 模式
在我的 dump 分析之旅中,曾经就遇到过一个案例,需要修改 GC 模式,比如说 并发模式 改成 非并发模式,那改完之后我如何验证呢?
第一种方式就是通过 x 命令去搜 coreclr 中的符号,比如下面这样:
0:000> x coreclr!GCConfig*
00007ffa`782763f6 coreclr!GCConfig::s_ConcurrentGC = true
00007ffa`7827b799 coreclr!GCConfig::s_ServerGC = false
虽然可以用 WinDbg 实现,但这种需要生成 dump 或者附加到进程中,那能不能在没有侵入的情况下获取 CoreCLR 当前的 GC 模式呢? 肯定是可以的,这又得需要借助 PerfView 啦, 它的底层逻辑是截获 Runtime/Start 这个 ETW 事件,在这个事件中有一个叫 StartupFlags 枚举,里面就记录着当前的 GC 模式。
为了方便讲述,在 *.csproj 中修改 GC 的模式为 Server 版,代码如下:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x86</Platforms>
</PropertyGroup>
</Project>
接下来启动 PerfView ,点击 Collect -> Collect 启动收集,然后把程序跑起来,停止收集后,我们在 Filter 中输入 Runtime/Start 事件,如果你的列表中没有 StartupFlags 列的话,记得在 Cols 上选择一下哦,截图如下:

从图中可以看到,当前的 StartupFlags=8392707 ,那这一串数字代表什么意思呢?这就需要到 CoreCLR 中找到它的枚举定义,接下来我们写段代码将它翻译出字符串形式。
internal class Program
{
static void Main(string[] args)
{
var value = "8392707";
Enum.TryParse<Test>(value, out var result);
var txt = result.ToString().Replace(", ", "\r\n");
Console.WriteLine(txt);
}
[Flags]
enum Test
{
STARTUP_CONCURRENT_GC = 0x1,
STARTUP_LOADER_OPTIMIZATION_MASK = (0x3 << 1),
STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN = (0x1 << 1),
STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN = (0x2 << 1),
STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST = (0x3 << 1),
STARTUP_LOADER_SAFEMODE = 0x10,
STARTUP_LOADER_SETPREFERENCE = 0x100,
STARTUP_SERVER_GC = 0x1000,
STARTUP_HOARD_GC_VM = 0x2000,
STARTUP_SINGLE_VERSION_HOSTING_INTERFACE = 0x4000,
STARTUP_LEGACY_IMPERSONATION = 0x10000,
STARTUP_DISABLE_COMMITTHREADSTACK = 0x20000,
STARTUP_ALWAYSFLOW_IMPERSONATION = 0x40000,
STARTUP_TRIM_GC_COMMIT = 0x80000,
STARTUP_ETW = 0x100000,
STARTUP_ARM = 0x400000,
STARTUP_SINGLE_APPDOMAIN = 0x800000,
STARTUP_APPX_APP_MODEL = 0x1000000,
STARTUP_DISABLE_RANDOMIZED_STRING_HASHING = 0x2000000
}
}
程序跑起来后,截图如下:

从图中可以清晰的看到,当前的 GC 模式为 CONCURRENT_GC & SERVER_GC,这和 WinDBG 的输出不约而同。
好了,本篇就聊这两个超实用的分析技巧,希望对大家有所帮助。
PerfView专题 (第八篇):洞察 C# 内存泄漏之寻找静态变量名和GC模式的更多相关文章
- 精华阅读第 13 期 |常见的八种导致 APP 内存泄漏的问题
本期是移动开发精英俱乐部的第13期文章,都是以技术为主,所以这里就不过多的进行赘述了,我们直接看干货内容吧!本文系ITOM管理平台OneAPM整理. 实际项目中的MVVM(积木)模式–序章 导读:开篇 ...
- PerfView专题 (第十篇):洞察 C# 终结队列引发的内存泄漏
一:背景 C# 程序内存泄漏的诱发因素有很多,但从顶层原理上来说,就是该销毁的 用户根 对象没有被销毁,从而导致内存中意料之外的对象无限堆积,导致内存暴涨,最终崩溃,这其中的一个用户根就是 终结器队列 ...
- PerfView专题 (第十一篇):使用 Diff 功能洞察 C# 内存泄漏增量
一:背景 去年 GC架构师 Maoni 在 (2021 .NET 开发者大会) [https://ke.segmentfault.com/course/1650000041122988/section ...
- PerfView专题 (第三篇):如何寻找 C# 中的 VirtualAlloc 内存泄漏
一:背景 上一篇我们聊到了如何用 PerfView 去侦察 NTHeap 的内存泄漏,这种内存泄漏往往是用 C 的 malloc 或者 C++ 的 new 分配而不释放所造成的,这一篇我们来聊一下由 ...
- PerfView专题 (第五篇):如何寻找 C# 托管内存泄漏
一:背景 前几篇我们聊的都是 非托管内存泄漏,这一篇我们再看下如何用 PerfView 来排查 托管内存泄漏 ,其实 托管内存泄漏 比较好排查,尤其是用 WinDbg,毕竟C#是带有丰富的元数据,不像 ...
- PerfView专题 (第七篇):如何洞察触发 GC 的 C# 代码?
一:背景 上一篇我们聊到了如何用 PerfView 洞察 GC 的变化,但总感觉还缺了点什么? 对,就是要跟踪到底是什么代码触发了 GC,这对我们分析由于 GC 导致的 CPU 爆高有非常大的参考价值 ...
- PerfView专题 (第四篇):如何寻找 C# 中程序集泄漏
一:背景 前两篇我们都聊到了非托管内存泄漏,一个是 HeapAlloc ,一个是 VirtualAlloc,除了这两种泄漏之外还存在其他渠道的内存泄漏,比如程序集泄漏,这一篇我们就来聊一聊. 二: 程 ...
- leakcanary内存泄漏:此篇有加了内存泄漏的apk demo
概括: ·用Android studio写一个demo ·配置leakcanary ·加入内存泄漏代码片段 ·安装apk 验证结果 ·源码地址 一.android ...
- 从一个微型例子看“C/C++的内存分配机制”和“数组变量名与指针变量名”(转)
C++的内存有五大分区:堆区.栈区.自由存储区.全局/静态存储区.常量存储区. 五个数据段:数据段.代码段.BSS段.堆.栈 内存分配方式有三种: 从静态存储区域分配.内存在程序编译的时候就已经分配好 ...
随机推荐
- django框架1
简介 1.前端 与用户打交道的界面 2.web框架 可以将前端和数据库整合到一起 3.数据库 专门用于存储数据 内容概要 纯手撸web框架 基于wsgiref模块 优化措施 动静态网 ...
- element-ui table组件使用v-if时的问题
element-ui项目中经常遇到需要使用v-if指令来根据情况动态显示隐藏某些列情况,这时就会出现滚动条样式异常.列错乱.列宽错乱等问题 解决办法:在el-table上添加:key="Ma ...
- atcoder abc 244
atcoder abc 244 D - swap hats 给定两个 R,G,B 的排列 进行刚好 \(10^{18}\) 次操作,每一次选择两个交换 问最后能否相同 刚好 \(10^{18}\) 次 ...
- 关于 GIN 的路由树
GIN 是一个 golang 常用的 Web 框架,它对 API 比较友好,源码注释也很明确明确,使用起来快速灵活,还有极高的容错率.标题中的路由我们可以简单理解为在浏览器中输入的页面地址,而&quo ...
- 从 CPU 讲起,深入理解 Java 内存模型!
Java 内存模型,许多人会错误地理解成 JVM 的内存模型.但实际上,这两者是完全不同的东西.Java 内存模型定义了 Java 语言如何与内存进行交互,具体地说是 Java 语言运行时的变量,如何 ...
- ASP.NET MVC之models_list传值view
单个用户在查看个人信息时,一个model就能解决 但是如果是管理员,就需要查看多个用户,此时我们从数据库读一页的用户数据(大致十条) 此时返回的就可能是一个泛型列表 所以除了基本的用户model外(比 ...
- HiEngine:可媲美本地的云原生内存数据库引擎
摘要:HiEngine与华为GaussDB (for MySQL)集成,将内存数据库引擎的优势带到云端,并与基于磁盘的引擎共存.HiEngine的性能比传统的以存储为中心的解决方案高出7.5倍. 本文 ...
- Image-Text Matching
重要性和意义: Image-text matching has received a large amount of interest since it associates different mo ...
- EEPROM存储电路(M24C64芯片)
电可擦写可编程只读存储器(Electrically Erasable Programmable Read-only Memory, EEPROM)实现掉电情况下保存数据,设计温湿度变送器采用M24C6 ...
- Redis基础课程讲义
Redis基础 课程内容 Redis入门 Redis数据类型 Redis常用命令 在Java中操作Redis 1. 前言 1.1 什么是Redis Redis是一个基于内存的key-value结构数据 ...