记一次 .NET 某工控软件 内存泄露分析

news2024/12/22 4:37:41

一:背景

1.讲故事

上个月 .NET调试训练营 里的一位老朋友给我发了一个 8G 的dump文件,说他的程序内存泄露了,一时也没找出来是哪里的问题,让我帮忙看下到底是怎么回事,毕竟有了一些调试功底也没分析出来,说明还是有一点复杂的,现实世界中的dump远比课上说的复杂的多。

还是那句话,找我分析是免费的,没有某软高额的工时费,接下来我们上 WinDbg 说话。

二:WinDbg 分析

1. 托管还是非托管泄露

这是我们首先就要做出的抉择,否则就会南辕北辙,可以使用 !address -summary & !eeheap -gc 来定位一下。


0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                   1311     7ffc`e2b37000 ( 127.988 TB)           99.99%
<unknown>                              4799        2`4f798000 (   9.242 GB)  74.19%    0.01%
Heap                                   3029        0`906fe000 (   2.257 GB)  18.12%    0.00%
Image                                  3435        0`2b530000 ( 693.188 MB)   5.43%    0.00%
Stack                                   226        0`11e00000 ( 286.000 MB)   2.24%    0.00%
Other                                    90        0`0025c000 (   2.359 MB)   0.02%    0.00%
TEB                                      75        0`00096000 ( 600.000 kB)   0.00%    0.00%
PEB                                       1        0`00001000 (   4.000 kB)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                            7990        2`e6964000 (  11.603 GB)  93.14%    0.01%
MEM_IMAGE                              3445        0`2b536000 ( 693.211 MB)   5.43%    0.00%
MEM_MAPPED                              220        0`0b61f000 ( 182.121 MB)   1.43%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                               1311     7ffc`e2b37000 ( 127.988 TB)           99.99%
MEM_COMMIT                             8158        1`cf52a000 (   7.239 GB)  58.11%    0.01%
MEM_RESERVE                            3497        1`4df8f000 (   5.218 GB)  41.89%    0.00%

0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000023ba303e940
generation 1 starts at 0x0000023ba2ebd0d0
generation 2 starts at 0x00000239a80f1000
ephemeral segment allocation context: none
...
Large object heap starts at 0x00000239b80f1000
         segment             begin         allocated              size
00000239b80f0000  00000239b80f1000  00000239bfe174a8  0x7d264a8(131228840)
0000023a6f050000  0000023a6f051000  0000023a73780800  0x472f800(74643456)
Total Size:              Size: 0xea9878f8 (3935860984) bytes.
------------------------------
GC Heap Size:            Size: 0xea9878f8 (3935860984) bytes.

从卦中的 MEM_COMMITGC Heap Size 这两个指标来看,主要还是托管内存泄露,虽然非托管内存也不小,大概率还是托管这边导致的,有了这些信息之后,后面就是看下 托管堆 到底都是些什么对象。


0:000> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
...
00007ffa2d7a1080  4923008    118152192 System.WeakReference
00007ffa2d725e70  2224022    125834760 System.Object[]
00007ffa2701de10  1044218    133659904 System.Windows.Documents.Paragraph
00007ffa2706b470  1045023    142123128 System.Windows.Documents.Run
00007ffa2706a9b0  2098480    151090560 System.Windows.Documents.TextTreeTextNode
00007ffa2d7267d0  1138661    159949302 System.Char[]
00007ffa2d7259c0  1231039    160962948 System.String
00007ffa29580cd8      214    165608376 MS.Internal.WeakEventTable+EventKey[]
00007ffa2d729750  2116556    169324480 System.Collections.Hashtable
00007ffa2d724478  2117718    209740224 System.Collections.Hashtable+bucket[]
00007ffa2706eb08  4175733    367464504 System.Windows.Documents.TextTreeTextElementNode
00007ffa2700ca48  2088016    384194944 System.Windows.ResourceDictionary
00007ffa2957fdc8  2344569    405666920 System.Windows.EffectiveValueEntry[]              

从卦中的 TotalSize 来看并没有明显的特征,但从 Count 看还是有一些蛛丝马迹的,比如 System.Windows.Documents.TextTreeTextElementNode 对象为什么高达 417w ? 为什么 System.Windows.Documents.TextTreeTextNode209w ? 虽然都是 WPF 框架的内部类,但从名字上看貌似和 文本类 控件有关系。

2. TextTreeTextElementNode 为什么没被回收

有了这些可疑信息,接下来就需要看下他们为什么没有被 GC 收掉?要想找到答案就需要抽几个 TextTreeTextElementNode 看下用户根是什么?可以使用 !dumpheap -mt xxx 找到 address 之后再用 !gcroot 观察一下。


0:000> !dumpheap -mt 00007ffa2706eb08
         Address               MT     Size
00000239a815f028 00007ffa2706eb08       88
00000239a815f080 00007ffa2706eb08       88     
00000239a815f2e8 00007ffa2706eb08       88     
00000239a815f340 00007ffa2706eb08       88     
00000239a8259f18 00007ffa2706eb08       88  
...

0:000>  !gcroot 0000023a637180e0
 !gcroot 0000023a637180e0
Thread e6c:
    000000aebe7fec20 00007ffa296c0298 System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)
        rsi: 
            ->  00000239a8101688 System.Windows.Threading.Dispatcher
            ->  0000023b4630e9a8 System.EventHandler
            ->  0000023b4630a990 System.Object[]
            ->  00000239a8425648 System.EventHandler
                ...

结果刷了半天都没刷完,还把 windbg 给弄死了,看样子这个引用链得有几十万哈。。。截图如下:

那遇到这种情况怎么办呢? 为了能够记录到所有的引用链,大家可以用 windbg 的 .logopen.logclose 命令将所有的输出记录到文本中,喝了杯咖啡之后,终于output完了,看文件有 81w 行,真的心累。

一眼望去大多是 TextTreeTextElementNode 和 TextTreeFixupNode 之间的交叉引用,还得耐点心慢慢往上翻,看看可有什么蛛丝马迹,经过仔细排查,发现有一个 RickTextBox 控件,截图如下:

从名字上来看,可能是想用 RichTextBox 记录日志,接下来看下 OperatorLogItemRichTextBox 这个类是怎么写的。


public sealed class OperatorLogItemRichTextBox : RichTextBox, IOperatorLogger
{
    private static readonly DependencyProperty MaximumLogCountProperty = DependencyProperty.Register("MaximumLogCount", typeof(int), typeof(OperatorLogItemRichTextBox), new PropertyMetadata(1024));

    private static readonly DependencyProperty VerboseBrushProperty = DependencyProperty.Register("VerboseBrush", typeof(Brush), typeof(OperatorLogItemRichTextBox), new PropertyMetadata(Brushes.Gray));

    private static readonly DependencyProperty DebugBrushProperty = DependencyProperty.Register("DebugBrush", typeof(Brush), typeof(OperatorLogItemRichTextBox), new PropertyMetadata(Brushes.Cyan));

    ...

    private static readonly DependencyProperty ExceptionBrushProperty = DependencyProperty.Register("ExceptionBrush", typeof(Brush), typeof(OperatorLogItemRichTextBox), new PropertyMetadata(Brushes.Magenta));

    private static readonly DependencyProperty SpecialBrushProperty = DependencyProperty.Register("SpecialBrush", typeof(Brush), typeof(OperatorLogItemRichTextBox), new PropertyMetadata(Brushes.Magenta));

    ...
}

从源码看,朋友在项目中实现了一个自定义的 RichTextBox 控件来实现日志记录,内存泄露问题应该就在这里。

有车的朋友都知道 4S 店有一个好的传统,那就是 只换不修,又简单又能轻松挣钱,所以我给朋友的建议是:把 OperatorLogItemRichTextBox 从项目中给踢掉,排查下还有没有内存泄露的问题。

终于在一周后,收到了朋友的反馈,问题也终于解决了,截图如下:

三: 总结

其实关于 RichTextBox 的问题我遇到过二次,上次是崩溃相关的,如果要用它来记录日志,建议还是用信得过的第三方富文本控件,自己实现的话,难免会踩到很多坑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/65947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hash与ZSet的常用命令以及其底层数据结构

目录hash类型命令hsethgethmsethmgethgetallhsetnxhdelhexitshincrby与hincrbyfloathkeys与hvalshlenhstrlen有序set型命令zaddzrange 与zrevrangezrangebyscore与zrevrangebyscorezcardzcountzscorezincrbyzrank 与 zrevrankzremzremrangebyrankzremrangebyscorezrangebylexzl…

MySQL学习笔记(十四)索引失效有哪些情况?

1.表和数据 CREATE TABLE t_user (id bigint(32) NOT NULL AUTO_INCREMENT,user_name varchar(40) DEFAULT NULL COMMENT 用户名,user_code varchar(40) DEFAULT NULL COMMENT 用户编号,phone varchar(11) DEFAULT NULL COMMENT 电话,age tinyint(3) DEFAULT NULL COMMENT 年龄…

微机----------------中断技术

目录 中断概述中断处理过程中断请求中断响应中断处理8086/8088中断中断类型中断优先级顺序⭐中断向量表中断处理过程中断概述 中断: 当CPU执行程序时,由于随机的事件引起CPU暂时停止正在执行的程序,而转去执行中断服务程序,处理完后又返回被终止的程序断点处继续执行,这个…

Bug系列路径规划算法原理介绍(四)——I-BUG 算法

本系列文章主要对Bug类路径规划算法的原理进行介绍&#xff0c;在本系列的第一篇文章中按照时间顺序梳理了自1986年至2018年Bug类路径规划算法的发展&#xff0c;整理了13种BUG系列中的典型算法&#xff0c;从本系列的第二篇文章开始依次详细介绍了其中具有代表性的BUG1、BUG2、…

数学基础从高一开始2、集合间的基本关系

高中数学人教 A 版必修一 集合间的基本关系 学习目标&#xff1a; (1)初步理解集合之间的包含与相等的含义; (2)能识别给定集合的子集和真子案&#xff0c;了解空集含义: (3)能进行自然语言、图形语言(Venn 图)、符号语言闻的转换&#xff0c;积萦抽象思维的经验&#xff0c; …

递归算法(及其衍生算法:缓存,分治,回溯)

文章目录一、初识递归二、缓存三、分治四、回溯一、初识递归 递归函数 终止条件 递归关系 终止条件&#xff1a; 当大问题被拆解成能轻松解决的小问题时&#xff0c;运行终止条件中的逻辑 递归关系&#xff1a; 定义如何将大问题拆解为小问题 例子&#xff1a;小名跑步。 …

这十一个副业在家就可以完成,疫情在家也有收入,建议收藏

2022年&#xff0c;谁还没有副业&#xff1f; 经过两年的疫情&#xff0c;我们都知道没有钱是一件非常不舒服的事情。现在的做法是&#xff1a;主营业务要求稳定&#xff0c;副业要求发展&#xff1b;好好发展副业是硬道理。 在过去的两年里&#xff0c;我一直在探索副业项目…

(六)Vue之数据代理

文章目录回顾Object.defineProperty方法数据属性valueenumerablewritableconfigurable访问器属性get()set(v: any)何为数据代理Vue中的数据代理Vue学习目录上一篇&#xff1a;&#xff08;六&#xff09;Vue之MVVC 回顾Object.defineProperty方法 Object.defineProperty方法的…

Chain Surfase Test - java 链表经典 OJ 面试题 - 巨细

效果图 LeetCode - 206. 反转链表 代码如下&#xff1a; /** Definition for singly-linked list. public class ListNode { int val;ListNode next;ListNode() {}ListNode(int val) { this.val val; }ListNode(int val, ListNode next) { this.val val; this.next next; …

K-Means++代码实现

K-Means代码实现 数据集 https://download.csdn.net/download/qq_43629083/87246495 import pandas as pd import numpy as np import random import math %matplotlib inline from matplotlib import pyplot as plt# 按文件名读取整个文件 data pd.read_csv(data.csv)class…

Minikube – 配置 Jenkins Kubernetes plugin

文章目录1. 配置 kubernetes credentials2. 安装 kubernets plugin3. 安装 docker 插件4. 连接 minikube 集群5. Pod template 参数6. Container template 参数7. 实例7.1 创建一个简单 pod7.2 pod name 变化7.3 指定 namespace7.4 volumes 挂载7.5 Liveness Probe 探针7.6 创建…

关于l2实时接口的功能分析

因为国内外股价的上涨都可以在界面上去查询&#xff0c;所以公司能准确地判断股价上涨&#xff0c;并适时买入、卖出&#xff0c;以此获得一定的利润。 l2实时接口还可以把以往的数据表示成一条折线&#xff0c;让公司在进行分析时更形象、更有参考意义。在连接界面后&#xf…

[附源码]Python计算机毕业设计Django校园订餐系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

星环科技TDS 2.4.0 发布: 数据开发、数据治理、数据运营套件能力再次升级

近日&#xff0c;星环科技大数据开发工具 Transwarp Data Studio 2.4.0版本重磅发布&#xff0c;新版本中数据开发、数据治理、数据运营三大套件能力全部升级&#xff0c;让数据开发更便捷、数据治理更高效、数据运营更智能。本次升级的核心能力如下&#xff1a; 数据开发套件…

【Opencv实战】高手勿入,Python使用Opencv+Canny实现边缘检测以及轮廓检测(详细步骤+源码分享)

前言 有温度 有深度 有广度 就等你来关注哦~ 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 在这次的案例实战中&#xff0c;我们将使用Python 3和OpenCV。我们将使用OpenCV&#xff0c;因为它是…

谷歌牛人发布小说式《算法图解》,竟被人扒下来,在GitHub开源了

今天给大家带来了一本算法方向的好书&#xff1a;巴尔加瓦&#xff08;Aditya Bhargava&#xff09;老师 著&#xff0c;袁国忠老师译的 《算法图解&#xff1a;像小说一样有趣的算法入门书》&#xff0c;网上有没有开源版本我不知道&#xff0c;我就看他内容不错所以推荐给大家…

蓄电池建模、分析与优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清…

[附源码]Python计算机毕业设计Django校园运动会管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

数据存储,详细讲解

✨数据存储&#xff0c;详细讲解&#x1f49c;数据类型的介绍&#xff1a;&#x1f499;整形的内存存储大小端介绍&#xff1a;&#x1f49b;浮点数的存储&#x1f49c;数据类型的介绍&#xff1a; 1.内置类型&#xff1a; char //字符数据类型&#xff08;1&#xff…

SpringBoot 之 AOP

前言&#xff1a; Spring 三大核心思想是啥&#xff0c;还记得不&#xff1f;IOC&#xff08;控制反转&#xff09;&#xff0c;DI&#xff08;依赖注入&#xff09;&#xff0c;AOP&#xff08;面向切面编程&#xff09;。回顾一下这三个东西&#xff1a; IOC&#xff1a;不考…