前言
该文的前置篇为:
https://www.cnblogs.com/aoximin/p/16839830.html
本文介绍性能排查。
正文
上一节是出现错误了,如何去排查具体问题。
这一节介绍一下性能排查。
还是上文的例子作为演示:https://buggyambfiles.blob.core.windows.net/bin/buggyamb_v1.1.zip
项目地址:https://github.com/ahmetmithat/buggyamb
本文实验的还是lldb 和 sos。
对比一下cpu 情况。
实验实施条件:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
请求前:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
点击请求后:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
这样对比还是很大的哈。
那么我们来看下啥子情况吧。
查看进程名:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
那么当cpu 高的时候进行抓取,一般抓取两个,两个间隔10秒左右。
为什么抓取两个呢? 因为好对比作用,更好定位,这个多实验实验就清楚了。
抓取命令:
/usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.30/createdump 108232 -f /tmp/coredump.manual.1.%d /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.30/createdump 108232 -f /tmp/coredump.manual.2.%d
两个命令间隔10秒。
我们知道这个createdump 是 dotcore runtime 自带的。
那么怎么知道他的位置呢?
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
这样可以查找到位置。
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
可以看到10秒后内存升高了。
那么就可以上一章的内容了,进入lldb。
lldb --core coredump.manual.1.108232
然后查看线程:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
看这个线程,发现和其他GC mode 不一样。
那么就有一个东西需要科普了,分别是cooperative 和 preemptive。
如果线程的 GC 模式设置为 “抢占”,则表示 GC 可以随时挂起此线程。 相比之下,协作模式意味着 GC 必须等待线程切换到抢占模式,然后才能挂起它。 当线程运行托管代码时,它处于协作模式。
这句话什么意思呢? 就是说这个线程在占用cpu的意思。那么cpu 高就应该看这个东西了。
setthread 14 然后切到这个线程。 这里就不解释了,都是上一章的东西。
然后调用一下clrstack。
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
然后来看一下干了什么?
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
感觉是在做字符串拼接啊。
那么这个时候是会造成cpu高和内存高的,那么要证明自己的猜想。
使用dso 查看一下。
Displays all managed objects found within the bounds of the current stack.
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
看下这个string,为什么看这个呢?因为这个string,和System.Data.DataRow 比较近,这个可以学习汇编可能跟容易理解。
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
查看了一下这个倒是有100m。
读取一下内存,看下里面是什么?
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
那么我们知道,第二次转储文件的时候内存是上述了的。
那么同样的操作在第二个里面执行:
lldb --core /tmp/coredump.manual.2.108232 setthread 15 dso
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
这里已经变成了string[]
用dumparray 00007f48e538a4b0 查看一下这个string[] 对象是啥?
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
看下第一个的string 对象的情况:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
读取一下内存:
memory read -c 384 00007f48c3bc8f68
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
这里就基本确认问题了。
但是这样去定位问题,其实是有点慢的。而且发现,这个定位在cpu 倒是一个不错的选择,但是定位内存显得不那么合理。
因为cpu不高的情况,但是内存高的情况,这个时候肯定就是有很多碎片没有回收,上面查的情况是根据执行去判断的。
统计的方法定位问题是比较快的。
两个里面查看统计:
dumpheap -stat
第一个:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
第二个:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
发现这个system.string 两个都很大,且变多了,而DataRow 也不少。
但是这里涨的又不成比例,比如这里对象涨了几百,但是内存涨了200m。
这个时候可能就怀疑 大型对象堆 (LOH) 的问题了。
那么查一下大于85000字节的数据统计。
dumpheap -stat -min 85000
第一个:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
第二个:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
运行 dumpheap -stat -min 85000 -live。 此命令仅显示根于某处的对象。 在此示例中,只有正确的对象实例 string 位于 LOH 中。
-live 就是活跃的意思,也就是应用程序正在使用的,不会被GC的。
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
这里有4个,看下这4个是啥吧。
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
然后查看一个的内存:
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
这样就定位到了。
但是还得查看一下这个对象位置在哪? 怎么办呢?用SOS的命令:gcroot
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
这个是源代码内部的,看的不清楚。
使用-all
![重新整理 .net core 实践篇 ———— linux上性能排查 [外篇]](http://www.itfaba.com/wp-content/themes/kemi/images/loading.gif)
这样就直接定位到行了。
原因就是string+=string,等于String.Concat(System.String[]) 造成大量string 对象复制堆积。
结
下一节介绍procDump 和 dotnet-dump,procDump 这个挺好用的,dotnet-dump 更为方便。基本是必学的。