Arthas线上分析诊断调优工具
以前我们要排查线上问题,通常使用的是jdk自带的调优工具和命令。最常见的就是dump线上日志,然后下载到本地,导入到jvisualvm工具中。这样操作有诸多不变,现在阿里团队开发的Arhtas工具,拥有非常强大的功能,并且都是线上的刚需,尤其是情况紧急,不方便立刻发版,适合临时处理危急情况使用。下面分两部分来研究JVM性能调优工具:
1.JDK自带的性能调优工具
虽然有了Arthas,但也不要忘记JDK自带的性能调优工具,在某些场景下,他还是有很大作用的。而且Arthas里面很多功能其根本就是封装了JDK自带的这些调优命令。
2.Arthas线上分析工具的使用
这一部分,主要介绍几个排查线上问题常用的方法。功能真的很强大,刚兴趣的猿媛可以研究其基本原理。之前跟我同事讨论,感觉这就像病毒一样,可以修改内存里的东西,真的还是挺强大的。
以上两种方式排查线上问题,没有优劣之分,如果线上不能安装Arthas就是jdk自带命令,如果jdk自带命令不能满足部分要求,又可以安装Arthas,那就使用Arthas。他们只是排查问题的工具,重要的是排查问题的思路。不管黑猫、白猫,能抓住耗子就是好猫。
一、JDK自带的调优工具
这里不是流水一样的介绍功能怎么用,就说说线上遇到的问题,我们通常怎么排查,排查的几种情况。
- 内存溢出,出现OutOfMemoryError,这个问题如何排查
- CPU使用猛增,这个问题如何排查?
- 进程有死锁,这个问题如何排查?
- JVM参数调优
下面来一个一个解决
1、处理内存溢出,报OutOfMemoryError错误
第一步:通过jmap -histo命令查看系统内存使用情况
使用的命令:
jmap -histo 进程号
运行结果:
num #instances #bytes class name
----------------------------------------------
1: 1101980 372161752 [B
2: 551394 186807240 [Ljava.lang.Object;
3: 1235341 181685128 [C
4: 76692 170306096 [I
5: 459168 14693376 java.util.concurrent.locks.AbstractQueuedSynchronizer$Node
6: 543699 13048776 java.lang.String
7: 497636 11943264 java.util.ArrayList
8: 124271 10935848 java.lang.reflect.Method
9: 348582 7057632 [Ljava.lang.Class;
10: 186244 5959808 java.util.concurrent.ConcurrentHashMap$Node
8671: 1 16 zipkin2.reporter.Reporter$1
8672: 1 16 zipkin2.reporter.Reporter$2
Total 8601492 923719424
num:序号
instances:实例数量
bytes:占用空间大小
class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]
通过这个命令,我们可以看出当前哪个对象最消耗内存。
上面这个运行结果是我启动了本地的一个项目,然后运行【jmap -histro 进程号】运行出来的结果,直接去了其中的一部分。通过这里我们可以看看大的实例对象中,有没有我们自定义的实例对象。通过这个可以排查出哪个实例对象引起的内存溢出。
除此之外,Total汇总数据可以看出当前一共有多少个对象,暂用了多大内存空间。这里是有约860w个对象,占用约923M的空间。
第二步:分析内存溢出,查看堆空间占用情况
使用命令
jhsdb jmap --heap --pid 进程号
比如,我本地启动了一个项目,想要查看这个项目的内存占用情况:
[root@iZ2pl8Z ~]# jhsdb jmap --heap --pid 28692
Attaching to process ID 28692, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.13+10-LTS-370
using thread-local object allocation.
Garbage-First (G1) GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2065694720 (1970.0MB)
NewSize = 1363144 (1.2999954223632812MB)
MaxNewSize = 1239416832 (1182.0MB)
OldSize = 5452592 (5.1999969482421875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 1048576 (1.0MB)
Heap Usage:
G1 Heap:
regions = 1970
capacity = 2065694720 (1970.0MB)
used = 467303384 (445.65523529052734MB)
free = 1598391336 (1524.3447647094727MB)
22.622093161955704% used
G1 Young Generation:
Eden Space:
regions = 263
capacity = 464519168 (443.0MB)
used = 275775488 (263.0MB)
free = 188743680 (180.0MB)
59.36794582392776% used
Survivor Space:
regions = 6
capacity = 6291456 (6.0MB)
used = 6291456 (6.0MB)
free = 0 (0.0MB)
100.0% used
G1 Old Generation:
regions = 179
capacity = 275775488 (263.0MB)
used = 186285016 (177.65523529052734MB)
free = 89490472 (85.34476470947266MB)
67.54951912187352% used
下面来看看参数的含义
堆空间配置信息
Heap Configuration:
/**
* 空闲堆空间的最小百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0 * 到100,默认值为 40。如果HeapFreeRatio < MinHeapFreeRatio,则需要进行堆扩容,扩容的时机应该在每次垃圾回收之后。
*/
MinHeapFreeRatio = 40
/**
* 空闲堆空间的最大百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0
* 到100,默认值为 70。如果HeapFreeRatio > MaxHeapFreeRatio,则需要进行堆缩容,缩容的时机应该在每次垃圾回收之后
*/
MaxHeapFreeRatio = 70
/**JVM 堆空间允许的最大值*/
MaxHeapSize = 2065694720 (1970.0MB)
/** JVM 新生代堆空间的默认值*/
NewSize = 1363144 (1.2999954223632812MB)
/** JVM 新生代堆空间允许的最大值 */
MaxNewSize = 1239416832 (1182.0MB)
/** JVM 老年代堆空间的默认值 */
OldSize = 5452592 (5.1999969482421875MB)
/** 新生代(2个Survivor区和Eden区 )与老年代(不包括永久区)的堆空间比值,表示新生代:老年代=1:2*/
NewRatio = 2
/** 两个Survivor区和Eden区的堆空间比值为 8,表示 S0 : S1 :Eden = 1:1:8 */
SurvivorRatio = 8
/** JVM 元空间的默认值 */
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
/** JVM 元空间允许的最大值 */
MaxMetaspaceSize = 17592186044415 MB
/** 在使用 G1 垃圾回收算法时,JVM 会将 Heap 空间分隔为若干个 Region,该参数用来指定每个 Region 空间的大小 */
G1HeapRegionSize = 1048576 (1.0MB)
G1堆使用情况
Heap Usage:
G1 Heap:
regions = 1970
capacity = 2065694720 (1970.0MB)
used = 467303384 (445.65523529052734MB)
free = 1598391336 (1524.3447647094727MB)
22.622093161955704% used
G1 的 Heap 使用情况,该 Heap 包含 1970 个 Region,结合上文每个 RegionSize=1M,因此 Capacity = Regions * RegionSize = 1970 * 1M = 1970M,已使用空间为 445.65M,空闲空间为 1524.34M,使用率为 22.62%。
G1年轻代Eden区使用情况
G1 Young Generation:
Eden Space:
regions = 263
capacity = 464519168 (443.0MB)
used = 275775488 (263.0MB)
free = 188743680 (180.0MB)
59.36794582392776% used
G1 的 Eden 区的使用情况,总共使用了 263 个 Region,结合上文每个 RegionSize=1M,因此 Used = Regions * RegionSize = 263 * 1M = 263M,Capacity=443M 表明当前 Eden 空间分配了 443 个 Region,使用率为 59.37%。
G1年轻代Survivor区使用情况和G1老年代使用情况:和Eden区类似
Survivor Space:
regions = 6
capacity = 6291456 (6.0MB)
used = 6291456 (6.0MB)
free = 0 (0.0MB)
100.0% used
G1 Old Generation:
regions = 179
capacity = 275775488 (263.0MB)
used = 186285016 (177.65523529052734MB)
free = 89490472 (85.34476470947266MB)
67.54951912187352% used
Survivor区使用情况和Eden区类似。 老年代参数含义和Eden区类似。
通过上面的命令,我们就能知道当前系统对空间的使用情况了,到底是老年代有问题还是新生代有问题。
第三步:导出dump内存溢出的文件,导入到jvisualvm查看
如果前两种方式还是没有排查出问题,我们可以导出内存溢出的日志,在导入客户端进行分析
使用的命令是:
jmap -dump:file=a.dump 进程号
或者是直接设置JVM参数
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./ (路径)
然后导入到jvisualvm中进行分析,方法是:点击文件-&g