记一次 .NET医疗布草API程序 内存暴涨分析

一:背景

1. 讲故事

我在年前写过一篇关于CPU爆高的分析文章 再记一次 应用服务器 CPU 暴高事故分析 ,当时是给同济做项目升级,看过那篇文章的朋友应该知道,最后的结论是运维人员错误的将 IIS 应用程序池设成 32bit 导致了事故的发生,这篇算是后续????????????,拖了好久才续上哈。

犹记得那些天老板天天找我们几个人开会,大概老板是在传导甲方给过来的压力,人倒霉就是这样,你说 CPU 爆高可怕吧,我硬是给摁下去了,好了,Memory 又爆高了,尼玛我又给摁下去了,接着数据库死锁又来了,你能体会到这种压力吗????? 就像我在朋友圈发的那样,程序再不跑我就要跑了。

所以有时候敬敬风水还是很有必要的,有点扯远了哈,这篇我们来看看程序的内存暴涨如何去排查,为了让你更有兴趣,来一张运维发的内存监控图。

从图中可以看出,9点开始内存直线暴涨,绝对惊心动魄,要是我的小诺安这样暴涨就好了????????????,接下来 windbg 说话。

二:windbg 分析

1. 说一下思路

内存暴涨了,最怕的就是 非托管层 出了问题,它的排查难度相比 托管层 要难10倍以上,所以遇到这类问题,先祈祷一下吧,gc堆也罢,loader堆也不怕,所以先看看是否 进程内存 ≈ gc堆内存 ?

2. 排查托管还是非托管

排查方式也很简单,通过 !address -summary 看看进程的已提交内存,如下输出:


0:000> !address -summary--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                261      7fb`4b151000 (   7.982 TB)           99.77%
MEM_RESERVE                             278        2`6aafc000 (   9.667 GB)  51.35%    0.12%
MEM_COMMIT                             2199        2`4a3a3000 (   9.160 GB)  48.65%    0.11%

可以看到已提交内存是 9.1G,接下来看下 gc 堆的大小,使用 !eeheap -gc 即可。


0:000> !eeheap -gc
Number of GC Heaps: 8
------------------------------
Heap 0 (0000000002607740)
generation 0 starts at 0x00000001aaaa5500
generation 1 starts at 0x00000001aa3fd070
generation 2 starts at 0x0000000180021000
Heap 7 (0000000002713b40)
generation 0 starts at 0x000000053b3a2c28
generation 1 starts at 0x000000053a3fa770
generation 2 starts at 0x0000000500021000------------------------------
GC Heap Size:            Size: 0x1fdfe58c8 (8556271816) bytes.

从最后一行输出中可以看到当前的占用是 8556271816 / 1024 /1024 /1024 = 7.9G ,太幸运了,果然是托管层出了问题,gc 上有大块脏东西,这下有救了 ????????????。

3. 查看托管堆

知道托管堆出了问题后,接下来就可以用 !dumpheap -stat 去gc堆上翻一翻。


0:000> !dumpheap -stat
Statistics:MT    Count    TotalSize Class Name
000007fef7b5c308    32867       788808 System.DateTime
000007fef7b5e598    33049       793176 System.Boolean
000007feec7301f8    30430      1217200 System.Web.HttpResponseUnmanagedBufferElement
000007fef7b56020     2931      3130928 System.Object[]
000007fef7b580f8   219398      5265552 System.Int32
000007fe9a0c5428    46423      7799064 xxx.Laundry.Entities.V_InvoiceInfo
000007fef7b59638   164418      7892064 System.Text.StringBuilder
000007fef7b56980   164713     10059852 System.Char[]
000007fef7b5a278     7351     26037217 System.Byte[]
000007fe9a0d8758       35     28326856 xxx.Laundry.Entities.V_ClothesTagInfo[]
0000000002536f50    76837     77016088      Free
000007fe9a327ab0    46534    312964608 xxx.Laundry.Entities.V_InvoiceClothesInfo[]
000007fe9a0c4868  2068912    397231104 xxx.Laundry.Entities.V_ClothesTagInfo
000007fef7b55b70 98986851   3483764540 System.String
000007fe9a10ef80 23998759   3839801440 xxx.Laundry.Entities.V_InvoiceClothesInfo
Total 126039641 objects

我去,托管堆上的 xxx.Laundry.Entities.V_InvoiceClothesInfo 对象居然高达 2399w ,占了大概 3.6G,这还不算其附属对象,对了,如果直接用 !dumpheap -mt xxx 输出 address 的话,很难进行UI中止,所以这里有一个小技巧,用 range 来限定一下,如下代码所示:


0:000> !dumpheap -mt 000007fe9a10ef80 0 0000000180027b30Address               MT     Size
0000000180027800 000007fe9a10ef80      160     
0000000180027910 000007fe9a10ef80      160     
0000000180027a20 000007fe9a10ef80      160     
0000000180027b30 000007fe9a10ef80      160     Statistics:MT    Count    TotalSize Class Name
000007fe9a10ef80        4          640 xxx.Laundry.Entities.V_InvoiceClothesInfo
Total 4 objects

4. 查找引用根

接下来用 !gcroot 随便找一个 address 查看它的引用链,看看它到底被谁引用着?


0:000> !gcroot 0000000180027800
HandleTable:00000000013715e8 (pinned handle)-> 000000058003c038 System.Object[]-> 00000004800238a0 System.Collections.Generic.List`1[[xxx.Laundry.APIService.Models.Common.BaseModel, xxx.Laundry.APIService]]-> 0000000317e01ae0 xxx.Laundry.APIService.Models.Common.BaseModel[]-> 000000028010caf0 xxx.Laundry.APIService.Models.Common.BaseModel-> 00000003014cbbd0 System.Collections.Generic.List`1[[xxx.Laundry.Entities.V_InvoiceInfo, xxx.Laundry.Entities]]-> 00000003014f3580 xxx.Laundry.Entities.V_InvoiceInfo[]-> 00000003014cd7f0 xxx.Laundry.Entities.V_InvoiceInfo-> 000000038cc49bf0 System.Collections.Generic.List`1[[xxx.Laundry.Entities.V_InvoiceClothesInfo, xxx.Laundry.Entities]]-> 000000038cc49c18 xxx.Laundry.Entities.V_InvoiceClothesInfo[]-> 0000000180027800 xxx.Laundry.Entities.V_InvoiceClothesInfoFound 1 unique roots (run '!GCRoot -all' to see all roots).

从输出中可以看到,它貌似被一个 List<BaseModel> 所持有,哈哈,总算找到了,接下来就简单了,直接用 !objsize 看一看它的 size 有多大?


0:000> !objsize 00000004800238a0
sizeof(00000004800238a0) = -1972395312 (0x8a6fa2d0) bytes (System.Collections.Generic.List`1[[xxx.Laundry.APIService.Models.Common.BaseModel, xxx.Laundry.APIService]])

看到上面的 -1972395312 了吗?我去,int 类型的 size 直接给爆掉了,果然是个大对象,就是你了。。。如果非要看大小也可以,写一个脚本遍历一下。

三:总结

知道是 List<BaseModel> 做的孽后,仔细阅读了源码才知道,原来是给用户第一次数据全量同步的时候,服务端为了加速将数据缓存在 List<BaseModel> 这个静态变量中,很遗憾的是并没有在合适的时机进行释放,造成了高峰期内存直线暴增,优化方案很简单,就是修改业务逻辑咯,增加释放内存的时机。

题外话

  • 如果你遇到的是这种 Strong Handles 的静态变量,也可以直接用可视化的 dotMemory 查看。

当然你要保证你有足够的内存,毕竟也算是小10G的dump ????, 我的 16G 内存一下子就被吃掉了。。。

  • 善于用 String 驻留池机制来优化,看看它的源码定义吧。

public sealed class String{[SecuritySafeCritical]public static string Intern(string str){if (str == null){throw new ArgumentNullException("str");}return Thread.GetDomain().GetOrInternString(str);}}

对于那些重复度很高的string,用驻留池机制可以节省千百倍的内存空间,至于为什么可以优化,可参考我之前的文章:字符串太占内存了,我想了各种奇思淫巧对它进行压缩 。

END

工作中的你,是否已遇到 ... 

1. CPU爆高

2. 内存暴涨

3. 资源泄漏

4. 崩溃死锁

5. 程序呆滞

等紧急事件,全公司都指望着你能解决...  危难时刻才能展现你的技术价值,作为专注于.NET高级调试的技术博主,欢迎微信搜索: 一线码农聊技术,免费协助你分析Dump文件,希望我能将你的踩坑经验分享给更多的人。

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

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

相关文章

自已做的第一个autoconf程序(不断完善中)

2019独角兽企业重金招聘Python工程师标准>>> 1、先写个简单的main函数&#xff0c;并按如下目录结构存放&#xff1a; timesync-- |--conf |--include |--lib |--src 2、在最上层目录下&#xff0c;执行autoscan&#xff0c;生成configure.scan&#xff0c;并改名为…

轻松看懂机器学习十大常用算法

通过本篇文章大家可以对ML的常用算法形成常识性的认识。没有代码&#xff0c;没有复杂的理论推导&#xff0c;仅是图解&#xff0c;介绍这些算法是什么以及如何应用&#xff08;例子主要是分类问题&#xff09;。以后有机会再对单个算法做深入地解析。今天的算法如下&#xff1…

MATLAB常用算法与应用实例分享来袭!

小天从大学开始接触数学建模&#xff0c;便开启资料收集功能。经过近几年的积累和沉淀&#xff0c;再加上对数学建模领域的深入研究&#xff0c;收集整理了丰富的数学建模资料&#xff0c;内容涵盖“MATLAB常用算法”&#xff0c;“MATLAB算法应用实例”等。截止到今天&#xf…

4个终于被破译的世界级密码

全世界有3.14 % 的人已经关注了数据与算法之美很多时候&#xff0c;一个设计精巧的密码就像数学难题一样&#xff0c;许许多多难以破解的密码让人青丝泛白&#xff0c;至今仍未见天日。不过&#xff0c;也有一些密码中的幸运儿&#xff0c;最终仍然迎来了真相大白的那天。秘密组…

java虚拟机工作原理图_Java虚拟机工作原理

首先我想从宏观上介绍一下Java虚拟机的工作原理。从最初的我们编写的Java源文件(.java文件)是如何一步步执行的&#xff0c;如下图所示&#xff0c;首先Java源文件经过前端编译器(javac或ECJ)将.java文件编译为Java字节码文件&#xff0c;然后JRE加载Java字节码文件&#xff0c…

奇异值的物理意义是什么?

全世界有3.14 % 的人已经关注了数据与算法之美矩阵奇异值的物理意义是什么&#xff1f;或者说&#xff0c;奇异值形象一点的意义是什么&#xff1f;把m*n矩阵看作从m维空间到n维空间的一个线性映射&#xff0c;是否&#xff1a;各奇异向量就是坐标轴&#xff0c;奇异值就是对应…

WPF使用Animation仿WeChat(微信)播放语音消息

WPF开发者QQ群&#xff1a; 340500857 前言WPF使用Animation仿WeChat&#xff08;微信&#xff09;播放语音消息&#xff1f;效果图&#xff1a;创建MyAnimationForever.cs如下&#xff1a;public class MyAnimationForever : Control{private static Storyboard MyStory;priva…

epublib java_使用Epublib处理epub文件 | 学步园

通过wlw发布在我的博客funba.cn希望csdn能重新支持wlwEpublib是一个用于处理epub文件的java类库&#xff0c;可以对epub文件进行读写处理&#xff1b;而且提供了一个阅读器&#xff0c;可以直接运行(java -jar 方式)&#xff0c;并可以打开epub类型的文件进行阅读。1、这个例子…

机器学习的最佳学习路线,就在这里!

AI这个词相信大家都非常熟悉&#xff0c;近几年来人工智能圈子格外热闹&#xff0c;光是AlphoGo就让大家对它刮目相看。今天小天就来跟大家唠一唠如何进军人工智能的第一步——机器学习。在机器学习领域&#xff0c;Python已经成为了主流。一方面因为这门语言简单易上手&#x…

io流图解 java_详细讲解JAVA中的IO流

一、流的概念流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中&#xff0c;管道是一条不间断的字节流&#xff0c;用来实现程序或进程间的通信&#xff0c;或读写外围设备、外部文件等。一个流&#xff0c;必有源端和目的端&#xff0c;它们可以是计算机内存的某些区域&a…

分段函数 左右 f'正 不等于f'负 则f'导数不存在。所以不能推出f连续是否

根据单侧极限定理。当求分界处函数时。如果 f正f负 那么 f存在。 如果 f正&#xff01;f负 f不存在。 如果 f正或者f负不存在。 则f不一定不存在。单侧极限是可导的充分非必要条件。转载于:https://www.cnblogs.com/friends-wf/articles/2380690.html

BPM与Workflow的区别

ItemworkflowBPM流程管理周期设计、执行流程全周期&#xff0c;包括梳理、监控、分析目标用户群体编程人员业务人员、IT技术人员平台化设施流程引擎内核流程、组织、权限、表单、规则、门户、监控、分析等一体化的平台设施应用范围部门级&#xff0c;十余支企业级多级流程层次&…

21个令程序员泪流满面的瞬间【第二弹】

【1】明明我只修改了一行代码... 【2】千万不要随便乱动旧项目【3】提交了错误的分支【4】断点调试BUG【5】当我删除一个无用的代码块【6】糟糕&#xff0c;无法捕获这个BUG【7】当我刚好有一个好点子却被人打断【8】不小心打开了遗留项目【9】费力地捕获一个已知Bug【10】…

大厂出品免费图标资源站

IconPark 字节跳动出品矢量图标样式的开源图标库编程导航开源仓库&#xff1a;https://github.com/liyupi/code-navIconPark 图标库是一个通过技术驱动矢量图标样式的开源图标库&#xff0c;可以实现根据单一 SVG 源文件变换出多种主题&#xff0c; 具备丰富的分类、更轻量的代…

数学不好、英语不好、非本专业,想学python数据分析,能安排吗?

全世界有3.14 % 的人已经关注了数据与算法之美“非本专业想转型做数据分析&#xff0c;有救吗&#xff1f;”“数学不好&#xff0c;英语不好&#xff0c;想学Python数据分析&#xff0c;有救吗&#xff1f;”“不懂Python数据分析到底是什么&#xff0c;有救吗&#xff1f;”我…

在 .NET 中使用 C# 处理 YAML

在 .NET 中&#xff0c;可以使用 YamlDotNet 类库解析和生成 YAML 文件。YamlDotNet &#xff1a;https://github.com/aaubry/YamlDotNetNuGet 下载&#xff1a;https://www.nuget.org/packages/YamlDotNet/帮助文档&#xff1a;https://github.com/aaubry/YamlDotNet/wiki序列…

java读取图片缩略方法_java 图片缩略图的两种方法

最近网上看到两种不同的java图片缩略图的绘制方案第一种&#xff0c;使用Graphics().drawImage按照一定的比例重新绘制图像。Java代码package com.image.suoluetu;import java.io.*;import java.awt.*;import java.awt.image.*;import com.sun.image.codec.jpeg.*;public class…

Python项目可以有多大?最多可以有多少行代码?

全世界有3.14 % 的人已经关注了数据与算法之美导读&#xff1a;总是看到有人说&#xff0c;动态一时爽&#xff0c;重构火葬场。然而这世界上有的是著名的开源项目&#xff0c; 也有像 Github、Instagram 这样流量巨大的知名网站是基于动态语言开发的&#xff0c;经过了这么多年…

从好买辞职后,为什么我会加入一家开源创业公司?

这是头哥侃码的第240篇原创熟悉我的朋友都知道&#xff0c;我是一个闲不住的人。工作之余&#xff0c;我不仅愿意把自己的经验拿出来与大家分享&#xff0c;而且还总是喜欢在字里行间中表达情感&#xff0c;并抒发命运的奇妙与无常。为什么&#xff1f;因为在我看来&#xff0c…

Apache 虚拟主机 VirtualHost 配置

为什么80%的码农都做不了架构师&#xff1f;>>> 虚拟主机 (Virtual Host) 是在同一台机器搭建属于不同域名或者基于不同 IP 的多个网站服务的技术. 可以为运行在同一物理机器上的各个网站指配不同的 IP 和端口, 也可让多个网站拥有不同的域名. Apache 是世界上使用…