PerfView专题 (第九篇):洞察 C# 中的 LOH 内存碎片化
一:背景
在 内存泄漏
的系列问题中,有一类问题是 内存碎片化
导致的,而且这种更容易发生在 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 双剑合璧的威力。
相关文章
- 【转载★架构】百万级访问量网站的技术准备工作
- 软件合作开发:2012年年底给苏州工业园区某家软件企业实施C#.NET软件开发系统框架的经验小结
- C# CharacterToBinary 将类似2进制字符串 10010110111 转换为数值型源码
- C#.NET 通用权限管理系统组件 大数据多表分页获取部分列的参考方法
- ASP.NET 实现轻量级的工作流[审批流程]
- 大公司业务流程审批组件【部门的员工—部门经理—部门副总—人力经理—人力副总】实现参考,强大的基础数据管理工具-C#.NET通用权限管理系统组件
- 节假日批量设置的C#.NET程序代码参考
- 多个业务子系统的集中统一管理用户权限,SQL脚本批量事务运行的参考代码
- 在c#.net通用权限管理系统组件里的 部门经理,分管副总 的管理方法参考
- 现在物价虽然高得离谱,但是内存条都白菜价格了,需要调整程序架构的思维“与时俱进” --- 改进系列之二
- 分享从带头拼死拼活开发软件项目到不去现场异地坐镇远程遥控照样可以把上海的软件项目管理好
- 整合公司3个网站后台管理子系统的经验总结 - 实现多系统的单点登录(ASP.NET + ASP)
- 1300多万条数据30G论坛大数据优化实战经验小结 - 2012年于浙江杭州西湖区
- 如何在自己的信息管理系统里集成第三方权限控制组件 - 开发一个好用稳定的开放组件
- 现在物价虽然高得离谱,但是内存条都白菜价格了,需要调整程序架构的思维“与时俱进” --- 改进系列之一
- 通用信息化开发平台 -- 通用权限管理系统改进登录日志显示功能
- 简单快速开发C\S架构程序用最简单的不分层最快的效率达到功能要求的例子程序FrmKnowledge日积月累功能的实现
- 简单快速开发C\S架构程序用最简单的不分层最快的效率达到功能要求的例子程序FrmCommnets 显示某个对象的评论列表的功能实现
- 简单快速开发C\S架构程序用最简单的不分层最快的效率达到功能要求的例子程序FrmCommnetList 所有评论列表的功能实现
- 简单快速开发C\S架构程序用最简单的不分层最快的效率达到功能要求的例子程序FrmCommentEdit 编辑评论的功能实现