当前位置: 首页 > news >正文

C#性能优化基础:内存诊断(dump)

  接上一篇:C#性能优化基础:垃圾回收机制

  本文说下怎么去查找内存问题,举个例子,我们有这样的一段程序:

    namespace ConsoleApp1{internal class Program{static List<Demo> Demos { get; } = new List<Demo>();static void Main(string[] args){while (true){Console.Write($"请输入要创建的对象个数(已有对象{Demos.Count}个):");var line = Console.ReadLine();if (int.TryParse(line, out var count)){while (count-- > 0){Demos.Add(new Demo() { Value7 = new Demo[] { new Demo() } });}}else{break;}}}}internal class Demo{public string Value1 { get; set; } = Guid.NewGuid().ToString("N");public string Value2 { get; set; } = Guid.NewGuid().ToString("N").PadRight(100000, '-');public int Value3 { get; set; } = new Random().Next(1, 100);public DateTime Value4 { get; set; } = DateTime.Now;public bool? Value5 { get; set; } = new Random().Next(0, 2) == 1;public DayOfWeek Value6 { get; set; } = (DayOfWeek)(new Random().Next(0, 7));public Demo[] Value7 { get; set; }}}

  代码很简单就不说了,我们在linux下去运行这段代码,为什么是linux呢,因为windows的下方法是差不多的,linux命令更方便展示些。

  首先,我们需要安装dotnet,我这边用的.net6,安装在/opt/dotnet目录,更多版本可以去官网下载sdk:https://dotnet.microsoft.com/en-us/download

  接着我们需要配置dotnet的安装目录:

    # 需要在/etc/dotnet/install_location中配置sudo mkdir /etc/dotnetecho /opt/dotnet > /etc/dotnet/install_location

  如果不配置,后续可能会出现下面的异常:

  image

  然后去官网下载我们需要的工具:

  下载dotnet-counters :https://learn.microsoft.com/zh-cn/dotnet/core/diagnostics/dotnet-counters

  下载dotnet-dump:https://learn.microsoft.com/zh-cn/dotnet/core/diagnostics/dotnet-dump

  我们可以直接下载linux下的可执行文件,把它放到dotnet的安装目录即可。

  然后我们把项目运行起来,通过dotnet-counters命令可以查看当前服务器上运行dotnet程序:sudo ./dotnet-counters ps

  image

  我这里采用run命令运行项目的,这样我们就拿到了前面的进程ID(那个demo的项目名)。

  image

  注:有人可能会想,如果只为了拿进程ID,我们可以同topps命令都可以,确实是的,但是那样拿到的命令不一定有用,因为它要求我们的dotnet程序和dotnet-counters在同一个域,比如程序运行在服务单元,但是dotnet-counters运行在shell,那么可能获取不到程序的信息,即上面的命令输出可能没有dotnet进程的信息。

  然后我们还可以通过dotnet-counters命令查看程序进程内部的内存变化:sudo dotnet-counters monitor --refresh-interval=1 -p 361706,结果大概如下图:

  image

  我们关注几个指标就好了:

    GC Heap Size (MB) :堆内存大小Gen X GC Count (Count / 1 sec):第X代垃圾数量Gen 0 Size (B):第X代垃圾大小LOH Size (B):大对象堆的大小

  接下来我们可以通过dotnet-dump来生成转储文件来查看内存中的数据了(转储文件可以理解为此刻内存的一个快照,把它收集到文件里面方便查看):

    # 生成转储文件sudo ./dotnet-dump collect -p 361706# 分析转储文件,core_20250906_175409是上面生成的转储文件sudo ./dotnet-dump analyze core_20250906_175409

  image

  分析查看命令常用的有下面这些(更多命令可以参考官网:https://learn.microsoft.com/en-us/dotnet/framework/tools/sos-dll-sos-debugging-extension)

    dumpheap:过滤、统计、打印内存数据信息-stat:统计内存数据,一般按照方法表展示(方法表可以理解为就是类型,一个类型的所有数据放在一起就是方法表)-mt:指定要展示的方法表-min:内存的最小值-max:内存的最大值-type:指定包含的类型,建议类型的完整形式,包括明明空间,如:System.String-live:只列出还活动的内存数据-strings:统计字符串数据的信息-short:将输出限制为只是每个对象的地址dumpobj:显示有关指定地址处的对象的信息。 DumpObj 命令显示对象的字段、EEClass 结构信息、方法表和大小。可简写成do-nofields:可阻止显示对象的字段,它对 String 这样的对象很有用。dumparray:检查数组对象的元素,可简写成da-start:选项指定开始显示元素的起始索引。-length:选项指定要显示的元素数量。-details:选项使用 dumpobj 和 dumpvc 格式显示元素的详细信息。-nofields:选项可阻止显示数组。 此选项仅在指定 -details 选项后可用。dumpvc:显示有关指定地址处的值类字段的信息。dc:查看字符串数据,对于大字符串比较有效gcroot:显示有关对指定地址处的对象的引用(或根)的信息

  接下来逐个展示一下他们的用法:

    # 统计每个方法表的对象个数和总内存dumpheap -stat# 统计每个方法表还活动的大对象的个数和总内存dumpheap -stat -min 85000 -live# 统计大字符串的个数和内存大小dumpheap -strings -min 85000# 查看某个方法表的数据dumpheap -mt 7f0af83bd2e0# 查看某个方法表的大对象数据dumpheap -mt 7f0af83bd2e0 -min 85000 -live# 查看某个类型的数据dumpheap -type System.String

  比如我们查看包含Demo类型的数据:dumpheap -type Demo

  image

  上图说我们Demo类有三个地方:List<Demo>(1个)、Demo[](3个)、Demo对象(2个),它们的地址也在上面列出来了

  我们现在逐个来对应他们的关系,最外层肯定是List<Demo>数组对象,所以我们可以查看一下链表数据(因为链表属于对象,所以用do查看):do 7f72c8008dd0

  image

  可以看到List<Demo>数组对象里面有三个字段,里面的_item字段记录的就是一个Demo[]数组对象(地址7f72c805eb30),这也解释了为什么会有三个数组对象的原因。

  我们接着去看_item字段里面是什么(因为是数组,所以用dumparray):dumparray 00007f72c805eb30

  image

  可以看到这个数组有四个元素,但是只有第一个有数据,其他都是null,我们明明至添加了一个,为什么有四个?读者可自行思考下。

  我们接着看第一个元素里面是什么(数组里面是Demo对象,所以用do查看):do 00007f72c8014ac8

  image

  可以看到对象里面的所有字段,我们可以逐个检查:

  Value3是int数值类型,值是78,Value6是美剧类型,值是4,都已经打印出来了。

  Value1Value2是字符串,可以通过do查看:

  image

  可以看到Value2的结果没有打印出来,因为结果太长了,我们可以换个方式打印,通过dc命令,通过上图得到Value2的长度是100000,那么我们执行:dc -c 100100 -w 100100 00007f72d7fff038 ,这样我们就把所有的字符串都打印出来了。

  Value4DateTime结构体,但是内存里面是一个地址,所以我们不能用do,应该用dumpvc 去查看:dumpvc 00007f72fe0c8668 00007f72c8014af0

  image

  可以看到它只有一个_dateDatalong类型数据,它其实就是DateTimeTick属性。

  Value5Nullable,它是机构体,所以应该通过dumpvc查看:dumpvc 00007f72fe0c8950 00007f72c8014af8

  image

  Value7是数组,我们可以用dumparray查看,这就回到上面的情况了,这里就不解释了

  最后,gcroot也可以演示一下,比如对于Value7,我们执行: gcroot 00007f72c805e948

  image

  它告诉我们地址是怎么关联引用的。

  

  总结

  本文只是介绍怎么去处理内存问题,如果碰到内存过高或者没有释放,我们可以通过dump去查原因。

  当然我们可以通过VS去分析,但是如果是开发环境还好,如果是生产环境,转储文件一般比较大,因为本来就是要处理内存大的原因,而转储文件就是内存的快照,自然就大了,文件一大就不方便搬运。。。

 

http://www.hskmm.com/?act=detail&tid=27501

相关文章:

  • 2025年企业级LLM内容安全防护指南:鉴冰AI FENCE流式网关技术深度解析
  • 完整教程:FPGA学习笔记——图像处理之亮度调节(Gamma)
  • Kubernetes Ingress:管理集群外部访问的入口网关
  • 搜索选讲
  • vue打包的项目,从根目录进去路由可访问,浏览器直接打开这个路由不可访问
  • IObit Uninstaller一款强大的卸载工具!IObit Uninstaller卸载工具,IObit Uninstaller下载安装教程
  • 网络配置不再难:4G/Wi-Fi/以太网/虚拟网卡全指南
  • 计算几何
  • 2025开关按钮厂家最新推荐榜:开关按钮,带灯开关按钮,防水开关按钮,防爆开关按钮,防腐开关按钮等全种类覆盖,高品质设计与卓越性能口碑之选
  • 一种排查java.lang.OutOfMemoryError: Metaspace的方法
  • First Blog Post
  • 本站点即将在2025年改变研究方向和目标
  • 实用指南:12_OkHttp初体验
  • (AE)Adobe After Effects 2025 视频后期制作软件!安装包永久免费免激H解锁版下载与图文详细安装教程!!
  • Postgresql主从配置
  • 乒乓球
  • 2025年工程管理软件系统推荐榜:交付管理/工程协同/工程管理/智慧工地管理系统
  • 《程序员修炼之道》 阅读笔记一
  • 大型行为模型LBM超越语言模型的技术解析
  • 2025工程管理软件系统推荐榜:技术赋能下的场景化解决方案全景
  • 【LVS入门宝典】LVS-TUN模式原理与配备:跨越网络界限的负载均衡解决方案
  • Java基础-Eclipse工具-面向对象(1)
  • Avalonia UI 投资 Wilderness Labs
  • BLE开发新体验:四种模式全解析,源码免费开放
  • JBoltAI V4 - 那年-冬季
  • 【EI检索】2025年智能决策与机器学习国际学术会议 (ICIDML 2025)
  • 10月9号
  • Qwen3技术报告
  • 赋能智慧监管:国标GB28181平台EasyGBS在明厨亮灶场景中的深度应用
  • CFD与FDM, FEM, FVM的关系?