一:背景

内存泄漏 的系列问题中,有一类问题是 内存碎片化 导致的,而且这种更容易发生在 LOH 上,因为它默认不开启 对象压缩,一般遇到这种情况,优先让朋友执行下面的代码应急。


GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();

后续再研究问题根源,这篇我们就来聊一聊如何用 PerfView 神器帮助我们寻找 内存碎片化 的根源。

二:碎片化洞察

1. WinDbg 的局限

为了方便讲述,先上一段造成 LOH内存碎片化 的测试代码。


internal class Program
{
static void Main(string[] args)
{
Test(); Console.ReadLine();
} public static List<byte[]> list = new List<byte[]>(); static void Test()
{
for (int i = 0; i < 50000; i++)
{
if (i % 2 == 0)
{
list.Add(new byte[85000 * 2]);
list[i] = null;
}
else
{
list.Add(new byte[85000]);
}
} Console.WriteLine("5w 数据插入完毕!");
}
}

代码逻辑非常简单,就是间隔释放其中的 byte[] 对象,让 Free 和 Live 对象成交错状, 可以用 WinDbg 观察如下:


0:009> !dumpheap
Address MT Size
...
00000000f0cd6028 000000000050ff60 170088 Free
00000000f0cff890 00007ffdb4a25490 85024
00000000f0d144b0 000000000050ff60 170088 Free
00000000f0d3dd18 00007ffdb4a25490 85024
00000000f0d52938 000000000050ff60 170088 Free
00000000f0d7c1a0 00007ffdb4a25490 85024
00000000f0d90dc0 000000000050ff60 170088 Free
00000000f0dba628 00007ffdb4a25490 85024
00000000f0dcf248 000000000050ff60 170088 Free
00000000f0df8ab0 00007ffdb4a25490 85024
00000000f0e0d6d0 000000000050ff60 170088 Free
00000000f0e36f38 00007ffdb4a25490 85024
00000000f0e4bb58 000000000050ff60 170088 Free
00000000f0e753c0 00007ffdb4a25490 85024
00000000f0e89fe0 000000000050ff60 170088 Free
00000000f0eb3848 00007ffdb4a25490 85024
00000000f0ec8468 000000000050ff60 170088 Free
00000000f0ef1cd0 00007ffdb4a25490 85024
00000000f0f068f0 000000000050ff60 170088 Free
00000000f0f30158 00007ffdb4a25490 85024
00000000f0f44d78 000000000050ff60 170088 Free
...

虽然用 WinDBG 可以轻松找出,但这里有一个非常大的局限,就是你不知道 Free 对象生前是什么东西,往往这时候就只能用 db,dc,du 看内存地址,在无计可施的情况下, PerfView 就可以大显威龙了。

2. PerfView 洞察

接下来我们打开PerfView,采用默认设置启动收集,稍等之后,点击 Memory -> GCStats 项,观察 LOH Frag % 列,如下图所示:

从图中的 LOH Frag % 列可以看出碎片化确实蛮高的,接下来我们就是找 Free 块生前是什么东西,如果能记录到 Free 生成是由谁分配的那该有多好呀!!!

哈哈,在 PerfView 中还真有这么一个视图叫 Gen 2 Object Deaths Stacks,如下图所示:

从名字上就能看到,这个视图记录的是 LOH 上那些已经死亡对象的生前 Stack,当然了,这是按权重计算的,如果是 Event 模式产生的就好了,那会记录所有的对象分配。

接下来双击 Gen 2 Object Deaths Stacks 再选中我们的应用程序,可以看到权重占比最高的是 System.Byte[] 对象,如下图所示:

接下来右键点击 Goto -> Goto Item in Callers 按钮,可以看到占比最高的是 Program.Test() 分配所致,高达 9396 个,如下图所示:

接下来就是调研 Program.Test() 方法,找出最后被 Free 的原因, 这就是 PerfView 和 WinDbg 双剑合璧的威力。

PerfView专题 (第九篇):洞察 C# 中的 LOH 内存碎片化的更多相关文章

  1. Egret入门学习日记 --- 第九篇(书中 2.7~2.8节 内容)

    第九篇(书中 2.7~2.8节 内容) 昨天记录到了 2.6节 ,那么今天就从 2.7节 开始. 这个 2.7节 有7个小段,有点长,总结一下重点: 1.调试项目的两种方法. 2.运行项目的两种窗口选 ...

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

    一:背景 上一篇我们聊到了如何去找 热点函数,这一篇我们来看下当你的程序出现了 非托管内存泄漏 时如何去寻找可疑的代码源头,其实思路很简单,就是在 HeapAlloc 或者 VirtualAlloc ...

  3. PerfView专题 (第一篇):如何寻找热点函数

    一:背景 准备开个系列来聊一下 PerfView 这款工具,熟悉我的朋友都知道我喜欢用 WinDbg,这东西虽然很牛,但也不是万能的,也有一些场景他解决不了或者很难解决,这时候借助一些其他的工具来辅助 ...

  4. Pytorch_第九篇_神经网络中常用的激活函数

    神经网络中常用的激活函数 Introduce 理论上神经网络能够拟合任意线性函数,其中主要的一个因素是使用了非线性激活函数(因为如果每一层都是线性变换,那有啥用啊,始终能够拟合的都是线性函数啊).本文 ...

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

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

  6. PerfView专题 (第十二篇):对 C# 下的 SDK 类库进行监控(大结局)

    一:背景 本篇是我们系列文章的最后一篇,前面的文章中大多是在 CLR Runtime 以及 OS 层面进行监控来发现各种可疑的程序问题,除了这两个层面,其实我们还可以对 SDK 中一些类进行洞察,比如 ...

  7. 第九篇 Flask 中的蓝图(BluePrint)

    第九篇 Flask 中的蓝图(BluePrint)   蓝图,听起来就是一个很宏伟的东西 在Flask中的蓝图 blueprint 也是非常宏伟的 它的作用就是将 功能 与 主服务 分开怎么理解呢? ...

  8. Flask最强攻略 - 跟DragonFire学Flask - 第九篇 Flask 中的蓝图(BluePrint)

    蓝图,听起来就是一个很宏伟的东西 在Flask中的蓝图 blueprint 也是非常宏伟的 它的作用就是将 功能 与 主服务 分开怎么理解呢? 比如说,你有一个客户管理系统,最开始的时候,只有一个查看 ...

  9. 解剖SQLSERVER 第九篇 OrcaMDF现在能通过系统DMVs显示元数据(译)

    解剖SQLSERVER 第九篇  OrcaMDF现在能通过系统DMVs显示元数据(译) http://improve.dk/orcamdf-now-exposes-metadata-through-s ...

随机推荐

  1. FFT 小记

    写在前面 \(Q:\) 为什么会心血来潮去学 FFT \(A:\) 当本蒟蒻还在努力消化凸包时:.所以本蒟蒻也来看一下 等等 摸头警告 .思维已经废了 About FFT FFT( \(Fast\ F ...

  2. ExtJS 布局-Column布局(Column layout)

    更新记录: 2022年6月1日 开始. 2022年6月4日 发布. 1.说明 使用列布局,可以将容器拆分为特定大小的列,并将子组件放置在这些列中. 可以设置子组件宽度值为: 百分比(相对父容器宽度) ...

  3. shell 问题记录

    工作中写了个 RestAPI 接口,然后想通过 crontab 任务,去定时调用接口.发现去拼接 post 请求真的不容易.对于单引号,双引号的使用.很懵,示例代码如下:对于 '$line' 处,单引 ...

  4. cookie 案例 记住上一次的访问时间

    需求:记住上一次的访问时间 第一次访问Servlet 提示 欢迎首次访问 之后的访问 都提示 您上次的访问时间为:"""""""& ...

  5. 入门Python数据分析最好的实战项目(一)分析篇

    数据初探 首先导入要使用的科学计算包numpy,pandas,可视化matplotlib,seaborn,以及机器学习包sklearn. python学习交流群:660193417### import ...

  6. Python: list列表的11个内置方法

    先来逼逼两句: 在实际开发中,经常需要将一组(不只一个)数据存储起来,以便后边的代码使用.在VBA中有使用数组,可以把多个数据存储到一起,通过数组下标可以访问数组中的每个元素.Python 中没有数组 ...

  7. 跨平台(32bit和64bit)的 printf 格式符 %lld 输出64位的解决方式

    问题描述 在 C/C++ 开发中,使用 printf 打印 64 位变量比较常用,通常在 32 位系统中使用 %lld 输出 64 位的变量,而在 64 位系统中则使用 %ld: 如果在 32 位系统 ...

  8. freeswitch拨打分机号源代码跟踪

    概述 freeswitch是一款非常好用的开源VOIP软交换平台. 之前我们有介绍过使用fs拨打分机号的方法,其中代码流程是比较复杂的,所以单独开一章介绍. fs拨打分机号,是使用send_dtmf接 ...

  9. Tapdata Cloud 2.1.2 来啦:大波细节已就绪!字段类型可批量修改、支持微信扫码登录、新增支持 Vika 为目标

    Tapdata Cloud cloud.tapdata.net 让数据实时可用 Tapdata Cloud 是国内首家异构数据库实时同步云平台,目前支持 Oracle.MySQL.PG.SQL Ser ...

  10. 基于SqlSugar的开发框架循序渐进介绍(11)-- 使用TypeScript和Vue3的Setup语法糖编写页面和组件的总结

    随着Vue3和TypeScript的大浪潮不断袭来,越来越多的Vue项目采用了TypeScript的语法来编写代码,而Vue3的JS中的Setup语法糖也越来越广泛的使用,给我们这些以前用弱类型的JS ...