记一次 .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: 1AsyncTimerCallbackCompletion 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 notif (compact){relocate_phase();compact_phase();}elsemake_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,后面再看看如何优化程序内存,毕竟现在托管堆处于损坏状态,也不好分析啦。

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

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

相关文章

C语言试题124之给一个不多于 5 位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字

C语言试题124之给一个不多于 5 位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字 47/100 发布文章 u011068702 未选择任何文件 new ✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模…

C#数组原来这么简单,你学废了吗?

文章目录 5.1 数组的概念5.2 数组声明与初始化5.3 数组的基本操作与排序5.4 多维数组小结5.1 数组的概念 1.数组与数组元素 数组是相同类型的对象的集合。 一个数组可以含有若干个下标变量(或称数组元素),下标也叫索引(Index),用来指出某个数组元素在数组中的位置。 数组…

数据基本类型以及相关举例

数据基本类型&#xff1a; 整型&#xff1a;字节型byte 短整型short 整型int 浮点类型&#xff1a;长整形long 浮点型 fioat 双精度性double 字符型&#xff1a;char 布尔型&#xff1a;boolean引用类型&#xff08; reference&#xff09;&#xff1a;类class 接口inte…

android--Activity有返回值的跳转

AndroidManifest.xml <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"package"com.hanqi.test4"><applicationandroid:allowBackup"true"and…

[转]ES6、ES7、ES8、ES9、ES10新特性一览 (个人整理,学习笔记)

目录 1.ES6新特性&#xff08;2015&#xff09; 1.1模块化(Module) 1.1.1导出(export) 1.1.2导入(import) 1.2箭头&#xff08;Arrow&#xff09;函数 1.2.1箭头函数的结构 1.3默认参数 1.4模板字符串 1.5.结构赋值 1.5.1数组的结构赋值 1.5.2对象的结构赋值 1.6延…

使用ML.NET+ONNX预训练模型整活B站经典《华强买瓜》

前言最近在看微软开源的机器学习框架ML.NET使用别人的预训练模型(开放神经网络交换格式.onnx)来识别图像&#xff0c;然后逛github发现一个好玩的repo。决定整活一期博客。首先还是稍微科普一下机器学习相关的知识&#xff0c;这一块.NET虽然很早就开源了ML.NET框架&#xff0c…

C语言试题125之一个 5 位数,判断它是不是回文数。即 12321 是回文数,个位与万位相同,十位与千位相同

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:一个 5 位数,判断它是不是回文数。即 12321 是回文数,个位与万位相同,十位与千位相…

不会C# Winform用户界面设计?是因为没有掌握这些控件(建议收藏)

文章目录 6.1 常用控件6.1.1 单选按钮(RadioButton)6.1.2 复选框(CheckBox)6.1.3 框架1. 面板控件Panel2. 分组框GroupBox3. TabControl控件6.1.4 应用实例6. 2 列表框和组合框6.2.1 列表框(ListBox)6.2.2 组合框(ComboBox )6.2.3 应用实例6.3 用户交互界面6.3.1 滚动条和进度条…

痞子衡嵌入式:ARM Cortex-M内核那些事(2)- 第一款微控制器

大家好&#xff0c;我是痞子衡&#xff0c;是正经搞技术的痞子。今天痞子衡给大家介绍的是第一款Cortex-M微控制器。 1.天生荣耀&#xff1a;ARM Cortex-M处理器由来 ARM公司自2004年推出ARMv7内核架构时&#xff0c;摒弃了以往"ARM数字"这种处理器命名方法&#xff…

SQL Server在更改计算机名后的设置

把原来的账号删除 再添加现有的账号 添加权限 搞定了上面的账号配置&#xff0c;接下来就是设置服务器名称 参考&#xff1a;http://www.cnblogs.com/EasonJim/p/6114249.html 后话&#xff1a;当初为了设置这个问题&#xff0c;选择了重装SQL Server&#xff0c;但是也是无用的…

Jupyter Notebook 入门指南

简介 Jupyter Notebook&#xff08;此前被称为 IPython notebook&#xff09;是一个交互式笔记本&#xff0c;支持运行 40 多种编程语言。 Jupyter Notebook 的本质是一个 Web 应用程序&#xff0c;便于创建和共享文学化程序文档&#xff0c;支持实时代码&#xff0c;数学方程…

C语言试题127之 100 之内的素数

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:求 100 之内的素数 2 、温馨提示 想获取更多C语言题目请猛搓这里==========》200个C语…

学弟学妹们,C#为啥学不好?是因为你没真正理解面向对象的思想!

文章目录 7.1面向对象的基本概念7.1.1 什么是面向对象编程7.1.2 面向对象编程的特点7.2 类7.2.1 类的概念7.2.2 类的声明7.2.3 类的成员7.2.4 类成员访问修饰符7.3 对象7.4 构造函数和析构函数7.4.1 构造函数7.4.2 析构函数7.5 方法7.5.1 方法的声明7.5.2 方法的参数7.5.3 静态…

MongoDB中的分组

一.MongoDB中的Count函数、Distinct函数以及分组 准备工作&#xff0c;插入一个班级的文档 > for(var i0;i<10;i){ ... db.Classes.insert({ClassName:"Class"i,_id:i}); ... } WriteResult({ "nInserted" : 1 }) > db.Classes.find() { "_i…

在 .NET 6 项目中使用 Startup.cs

对于 .NET 6 项目&#xff0c;现在已经找不到 Startup.cs 文件。默认情况下&#xff0c;此文件已经被删除&#xff0c;并且 Program.cs 是配置依赖注入服务和 Middleware 的新位置。但是&#xff0c;有些人可能更喜欢使用 Startup.cs , 并且我也是&#xff0c;可能已经习惯了&a…

编写iptables脚本实现IP地址、端口过滤

实验案例&#xff1a;公司使用一台运行RHEL5系统的服务器作为网关&#xff0c;分别连接三个网络&#xff0c;其中LAN1为普通员工电脑所在的局域网&#xff0c;LAN2为DNS缓存服务器所在的局域网。eth0通过10M光纤接入Internet。为了有效的管理网络环境及增强内部网络的安全性&am…

让VS Code 支持 Jupyter Notebook

一、Jupyter Notebook Jupyter Notebook是基于网页的用于交互计算的应用程序。其可被应用于全过程计算&#xff1a;开发、文档编写、运行代码和展示结果。——Jupyter Notebook官方介绍。 Jupyter Notebook是以网页的形式打开&#xff0c;可以在网页页面中直接编写代码和运行代…

C语言试题128之对 10 个数进行排序

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:对 10 个数进行排序 分析:可以利用选择法,即从后 9 个比较过程中,选择一个最小的与…

JavaScript设计模式

JavaScript的设计模式&#xff1a; 1、单例模式 单例模式&#xff08;Singleton&#xff09;&#xff0c;整个运行期间只会被初始化一次。该模式简单易懂&#xff0c;运用也很广泛。可以用它来聚合公共的方法&#xff0c;形成一个工具类&#xff0c;对外提供api。 var single …

Windows 11 23H2 25145 推送!全新隐私设置和 OneDrive 体验

面向 Dev 频道的 Windows 预览体验成员&#xff0c;微软现已推送 Windows 11 预览版 Build 25145。主要变化1.微软宣布为 Windows 11 设置引入全新 OneDrive 体验&#xff0c;您可以在设置中查看 OneDrive 云存储服务的订阅详情&#xff0c;包括付费方式、容量大小和定期付费等…