记一次 .NET 差旅管理后台 CPU 爆高分析
一:背景
1. 讲故事
前段时间有位朋友在微信上找到我,说他的 web 系统 cpu 运行一段时候后就爆高了,让我帮忙看一下是怎么回事,那就看吧,声明一下,我看 dump 是免费的,主要是锤炼自己技术,没有某软工程师高额的技术分析费。
闲话不多说,我们上 windbg 说话。
二:WinDbg 分析
1. CPU 真的爆高吗
是否真的爆高,我们得自己先验证下,使用 !tp 命令看一下即可。
0:065> !tp
CPU utilization: 81%
Worker Thread: Total: 32 Running: 7 Idle: 25 MaxLimit: 8191 MinLimit: 32
Work Request in Queue: 1
AsyncTimerCallbackCompletion TimerInfo@018eedc8
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 4 Free: 4 MaxFree: 64 CurrentLimit: 4 MaxLimit: 1000 MinLimit: 32
从卦象看,确实存在 CPU 爆高的情况,根据过往经验,托管程序爆高大多是因为GC触发所致,但触发 GC 的原因千奇百怪,毕竟在 clr 层面 GC 触发的原因高达 14 种,代码如下:
static const char* const str_gc_reasons[] =
{
"alloc_soh",
"induced",
"lowmem",
"empty",
"alloc_loh",
"oos_soh",
"oos_loh",
"induced_noforce",
"gcstress",
"induced_lowmem",
"induced_compacting",
"lowmemory_host",
"pm_full_gc",
"lowmemory_host_blocking"
};
2. 真的是 GC 触发吗
验证当前程序是否为 GC 触发,方式有很多,可以用 !t 或者 !t -special,但这两种方式不是特别准,最准的就是根据GC模式直接到 CLR 里去搜全局变量 clr!SVR::gc_heap::gc_started 的值就可以了,参考如下:
0:038> dp clr!SVR::gc_heap::gc_started L1
712d3190 00000001
可以看到,此时的 gc_started=1,说明 GC 是触发状态,接下来可以从所有的线程栈中搜 garbage_collect 或者 gc1 什么的关键词即可。
0:038> k
# ChildEBP RetAddr
00 0318f934 70de8248 clr!SVR::gc_heap::relocate_survivor_helper+0x1ea
01 0318f944 70de83df clr!SVR::gc_heap::relocate_survivors_in_plug+0x24
02 0318f970 70de84ac clr!SVR::gc_heap::relocate_survivors_in_brick+0x70
03 0318f9a8 70de830b clr!SVR::gc_heap::relocate_survivors+0xe4
04 0318fa00 70de218a clr!SVR::gc_heap::relocate_phase+0xb9
05 0318fbb4 70de18bf clr!SVR::gc_heap::plan_phase+0x136e
06 0318fbec 70de1d49 clr!SVR::gc_heap::gc1+0x101
07 0318fc3c 70de1421 clr!SVR::gc_heap::garbage_collect+0x746
08 0318fc58 70ddacde clr!SVR::gc_heap::gc_thread_function+0x14a
09 0318fc6c 70ddac6f clr!SVR::gc_heap::gc_thread_stub+0x72
0a 0318fc80 770a6a14 clr!GCThreadStub+0x1f
0b 0318fc94 77e4a9ef kernel32!BaseThreadInitThunk+0x24
0c 0318fcdc 77e4a9ba ntdll!__RtlUserThreadStart+0x2f
0d 0318fcec 00000000 ntdll!_RtlUserThreadStart+0x1b
从卦象看,gc_thread_stub 表示当前是一个 GC 线程,它正在处于 relocate_phase 阶段,这表明当前是一个 压缩回收,GC回收流程图可以看下官方文档。
GarbageCollectGeneration()
{
SuspendEE();
garbage_collect();
RestartEE();
}
garbage_collect()
{
generation_to_condemn();
gc1();
}
gc1()
{
mark_phase();
plan_phase();
}
plan_phase()
{
// actual plan phase work to decide to
// compact or not
if (compact)
{
relocate_phase();
compact_phase();
}
else
make_free_lists();
}
在这个阶段,托管堆也会是损坏状态,你可以用 !dumpheap -stat 验证下。
0:038> !dumpheap -stat
The garbage collector data structures are not in a valid state for traversal.
It is either in the "plan phase," where objects are being moved around, or
we are at the initialization or shutdown of the gc heap. Commands related to
displaying, finding or traversing objects as well as gc heap segments may not
work properly. !dumpheap and !verifyheap may incorrectly complain of heap
consistency errors.
Object <exec cmd="!ListNearObj /d 03301000">03301000</exec> has an invalid method table.
3. 为什么会出现压缩回收
一般来说,GC 分清除和压缩回收,后者属于一种重量级操作,很伤GC,在临时段上还稍微好一些,接下来我们看下当前 GC 是在回收哪一代?可以到 CLR 里面去查一下判决代字段。clr!WKS::GCHeap::GcCondemnedGeneration。
0:038> dp clr!SVR::GCHeap::GcCondemnedGeneration L1
712d79d8 00000002
糟了,结果是个 2,这个 2 表示 fullGC, 也就是全量回收,大多对应着 gc_reason=lowmem 的情况,也就是内存不足。
4. 真的内存不足吗
要想找到答案,我们用 !address -summary 看下当前的虚拟内存情况。
0:038> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown> 1835 dce6e000 ( 3.452 GB) 91.56% 86.29%
Image 842 f436000 ( 244.211 MB) 6.33% 5.96%
Free 312 eba5000 ( 235.645 MB) 5.75%
Stack 451 2d80000 ( 45.500 MB) 1.18% 1.11%
Heap 72 2342000 ( 35.258 MB) 0.91% 0.86%
TEB 150 96000 ( 600.000 kB) 0.02% 0.01%
Other 7 4e000 ( 312.000 kB) 0.01% 0.01%
PEB 1 1000 ( 4.000 kB) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE 2051 dd635000 ( 3.459 GB) 91.76% 86.48%
MEM_IMAGE 1267 11ad1000 ( 282.816 MB) 7.33% 6.90%
MEM_MAPPED 40 2345000 ( 35.270 MB) 0.91% 0.86%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT 2604 cbe7d000 ( 3.186 GB) 84.51% 79.65%
MEM_RESERVE 754 255ce000 ( 597.805 MB) 15.49% 14.60%
MEM_FREE 312 eba5000 ( 235.645 MB) 5.75%
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
<unknown> 3300000 20087000 ( 512.527 MB)
Image 6f819000 f5f000 ( 15.371 MB)
Free fea50000 1590000 ( 21.562 MB)
Stack 3110000 7a000 ( 488.000 kB)
Heap 3bc80000 621000 ( 6.129 MB)
TEB fe6e5000 1000 ( 4.000 kB)
Other fea10000 33000 ( 204.000 kB)
PEB fea49000 1000 ( 4.000 kB)
从卦象看,当前的 MEM_COMMIT=3.186G, 最大的Free块 Free=15.371MB,再根据之前展示的内存地址,我们发现这个程序是 32bit ,跑了 64bit 机器上,这种情况下程序最多可占用 4G 内存空间,虽然 MEM_RESERVE= 597.805 MB, 但这种 RESERVE 是零散的,本质上来说此时的程序处于虚拟地址紧张,由于 虚拟地址 紧张,导致 GC 在不断的做 全量内存 回收。
三:总结
根据上面的分析, GC 触发的原因主要还是 32bit 程序的 4G 内存限制所致, 导致 GC 在不停的做全量回收,这种场景真的让 GC 很尴尬,优先解决办法就是将程序改成 64bit,后面再看看如何优化程序内存,毕竟现在托管堆处于损坏状态,也不好分析啦。

记一次 .NET 差旅管理后台 CPU 爆高分析的更多相关文章
- 记一次 .NET 车联网云端服务 CPU爆高分析
一:背景 1. 讲故事 前几天有位朋友wx求助,它的程序CPU经常飙满,没找到原因,希望帮忙看一下. 这些天连续接到几个cpu爆高的dump,都看烦了,希望后面再来几个其他方面的dump,从沟通上看, ...
- 记一次 .NET 某电子病历 CPU 爆高分析
一:背景 1.讲故事 前段时间有位朋友微信找到我,说他的程序出现了 CPU 爆高,帮忙看下程序到底出了什么情况?图就不上了,我们直接进入主题. 二:WinDbg 分析 1. CPU 真的爆高吗? 要确 ...
- 记一次 .NET 某资讯论坛 CPU爆高分析
大概有11天没发文了,真的不是因为懒,本想前几天抽空写,不知道为啥最近求助的朋友比较多,一天都能拿到2-3个求助dump,晚上回来就是一顿分析,有点意思的是大多朋友自己都分析了几遍或者公司多年的牛皮藓 ...
- 记一次 .NET 某电商交易平台Web站 CPU爆高分析
一:背景 1. 讲故事 已经连续写了几篇关于内存暴涨的真实案例,有点麻木了,这篇换个口味,分享一个 CPU爆高 的案例,前段时间有位朋友在 wx 上找到我,说他的一个老项目经常收到 CPU > ...
- 记一次 .NET 某机械臂智能机器人控制系统MRS CPU爆高分析
一:背景 1. 讲故事 这是6月中旬一位朋友加wx求助dump的故事,他的程序 cpu爆高UI卡死,问如何解决,截图如下: 在拿到这个dump后,我发现这是一个关于机械臂的MRS程序,哈哈,在机械臂这 ...
- 记一次 .NET 某智能交通后台服务 CPU爆高分析
一:背景 1. 讲故事 前天有位朋友加微信求助他的程序出现了CPU爆高的问题,开局就是一个红包,把我吓懵了! 由于是南方小年,我在老家张罗处理起来不方便,没有第一时间帮他处理,朋友在第二天上午已经找出 ...
- 记一次 .NET游戏站程序的 CPU 爆高分析
一:背景 1. 讲故事 上个月有个老朋友找到我,说他的站点晚高峰 CPU 会突然爆高,发了两份 dump 文件过来,如下图: 又是经典的 CPU 爆高问题,到目前为止,对这种我还是有一些经验可循的. ...
- 记一次 .NET 某医院HIS系统 CPU爆高分析
一:背景 1. 讲故事 前几天有位朋友加 wx 抱怨他的程序在高峰期总是莫名其妙的cpu爆高,求助如何分析? 和这位朋友沟通下来,据说这问题困扰了他们几年,还请了微软的工程师过来解决,无疾而终,应该还 ...
- 记一次 .NET 某旅行社Web站 CPU爆高分析
一:背景 1. 讲故事 前几天有位朋友wx求助,它的程序内存经常飙升,cpu 偶尔飙升,没找到原因,希望帮忙看一下. 可惜发过来的 dump 只有区区2G,能在这里面找到内存泄漏那真有两把刷子..., ...
随机推荐
- Java第十五周作业
Cola公司的雇员分为以下若干类:(知识点:多态) [必做题]• 4.1 ColaEmployee :这是所有员工总的父类,属性:员工的姓名,员工的生日月份.方法:getSalary(int mont ...
- Apollo的docker配置详解步骤
Apollo 的docker配置 基础环境 centOS7 + Docker服务 + mysql服务 1. 下载Apollo的包 git clone https://github.com/ctripc ...
- Shell 脚本编程最佳实践
前言 由于工作需要,最近重新开始拾掇shell脚本.虽然绝大部分命令自己平时也经常使用,但是在写成脚本的时候总觉得写的很难看.而且当我在看其他人写的脚本的时候,总觉得难以阅读.毕竟shell脚本这个东 ...
- app嵌套页
Wdatepicker
- gogin web框架部署学习
首先去git上面找了一个gin框架拿来学习gin web开发: flipped-aurora/gin-vue-admin: 基于vite+vue3+gin搭建的开发基础平台(已完成setup语法糖版本 ...
- Caused by: java.lang.Exception: No native library is found for os.name=Mac and os.arch=aarch64. path=/org/sqlite/native/Mac/aarch64
编译项目报错: Caused by: java.lang.Exception: No native library is found for os.name=Mac and os.arch=aarch ...
- C# 编写一个简单易用的 Windows 截屏增强工具
半年前我开源了 DreamScene2 一个小而快并且功能强大的 Windows 动态桌面软件.有很多的人喜欢,这使我有了继续做开源的信心.这是我的第二个开源作品 ScreenshotEx 一个简单易 ...
- 2021夏季学期华清大学EE数算OJ2:难缠的店长
2021年夏季学期华清大学电子系数算oj2题解 某知名oier锐评蒟蒻的oj1题解: 话不多说,进入oj2题解: 难缠的oj 之 难缠的店长 当时读完我已经因为无良甲方的行为出离愤怒了!但是做题还是要 ...
- Java高并发-多线程基础
一.什么是线程 线程是进程内的执行单元. 二.线程的基本操作 2.1 状态与操作 2.2 新建线程 Thread t1 = new Thread(new CreateThread()); t1.sta ...
- Java中如何快捷的创建不可变集合
在Java 9中又新增了一些API来帮助便捷的创建不可变集合,以减少代码复杂度. 本期配套视频:Java 9 新特性:快速定义不可变集合 常规写法 以往我们创建一些不可变集合的时候,通常是这样写的: ...