天天看点

JDK自带的调优工具

这里不是流水一样的介绍功能怎么用,就说说线上遇到的问题,我们通常怎么排查,排查的几种情况。

1.内存溢出,出现OutOfMemoryError,这个问题如何排查

2.CPU使用猛增,这个问题如何排查?

3.进程有死锁,这个问题如何排查?

4.JVM参数调优

第一步:通过jmap -histo命令查看系统内存使用情况

使用的命令:

jmap -histo -pid(进程id)

JDK自带的调优工具

通过这个命令,我们可以看出当前哪个对象最消耗内存。

上面这个运行结果是我启动了本地的一个项目,然后运行【jmap -histro 进程号】运行出来的结果,直 接去了其中的一部分。通过这里我们可以看看大的实例对象中,有没有我们自定义的实例对象。通过这 个可以排查出哪个实例对象引起的内存溢出。

除此之外, Total汇总数据可以看出当前一共有多少个对象,暂用了多大内存空间。这里是有约860w个 对象,占用约923M的空间。

第二步:分析内存溢出,查看堆空间占用情况

使用命令

jmap -heap -pid (进程id)

比如,我本地启动了一个项目,想要查看这个项目的内存占用情况

[root ~]# jmap -heap 15271           
Attaching to process ID 15271, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.77-b03

using thread-local object allocation.
Garbage-First (G1) GC with 2 thread(s)



Heap Configuration:
MinHeapFreeRatio
MaxHeapFreeRatio
MaxHeapSize
NewSize
MaxNewSize
OldSize
NewRatio
SurvivorRatio
MetaspaceSize



= 40
= 70
= 536870912 (512.0MB)
= 1363144 (1.2999954223632812MB)
= 321912832 (307.0MB)
= 5452592 (5.1999969482421875MB)
= 2
= 8
= 134217728 (128.0MB)

CompressedClassSpaceSize = 1073741824 (1024.0MB)


MaxMetaspaceSize
G1HeapRegionSize


= 134217728 (128.0MB)
= 1048576 (1.0MB)


Heap Usage:
G1 Heap:
regions  = 512
capacity = 536870912 (512.0MB)
used     = 283115632 (270.00010681152344MB)
free     = 253755280 (241.99989318847656MB)
52.73439586162567% used
G1 Young Generation:
Eden Space:
regions  = 132
capacity = 305135616 (291.0MB)
used     = 138412032 (132.0MB)
free     = 166723584 (159.0MB)
45.36082474226804% used
Survivor Space:
regions  = 32
capacity = 33554432 (32.0MB)
used     = 33554432 (32.0MB)
free     = 0 (0.0MB)
100.0% used
G1 Old Generation:
regions  = 110
capacity = 198180864 (189.0MB)


used
free


= 111149168 (106.00010681152344MB)
= 87031696 (82.99989318847656MB)

56.08471259868965% used

48916 interned Strings occupying 5031568 bytes.           

下面来看看参数的含义

堆空间配置信息

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
/** JVM 元空间的默认值 */ MetaspaceSize	= 8

= 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
/** JVM 元空间允许的最大值 */
MaxMetaspaceSize         = 17592186044415 MB
/** 在使用 G1 垃圾回收算法时,JVM 会将 Heap 空间分隔为若干个 Region,
* 该参数用来指定每个 Region 空间的大小
*/
G1HeapRegionSize         = 1048576 (1.0MB)           

G1堆使用情况

G1 Heap:
regions  = 512
capacity = 536870912 (512.0MB)
used     = 283115632 (270.00010681152344MB)
free     = 253755280 (241.99989318847656MB)
52.73439586162567% used

G1 的 Heap 使用情况,该 Heap 包含 512 个 Region,结合上文每个 RegionSize=1M,
因此     
Capacity = Regions * RegionSize = 512 * 1M = 1970M,
已使用空间为270.00010681152344MB,
空闲空间为 241.99989318847656MB,
使用率为 52.73439586162567%。           

G1年轻代Eden区使用情况

G1 Young Generation:
Eden Space:
regions  = 132
capacity = 305135616 (291.0MB)
used     = 138412032 (132.0MB)
free     = 166723584 (159.0MB)
45.36082474226804% used           

G1年轻代Survivor区使用情况

Survivor Space:
regions  = 32
capacity = 33554432 (32.0MB)
used
free	= 33554432 (32.0MB)
= 0 (0.0MB)
100.0% used           

G1老年代使用情况

G1 Old Generation:
regions  = 110
capacity = 198180864 (189.0MB)
used
free	= 111149168 (106.00010681152344MB)
= 87031696 (82.99989318847656MB)
56.08471259868965% used           

通过上面的命令,我们就能知道当前系统堆空间的使用情况了,到底是老年代有问题还是新生代有问 题。

我们可以通过Jstack找出占用cpu最高的线程的堆栈信息,下面来一步一步分析。

第一步:运行代码,使用top命令查看cpu占用情况

第二步:使用top -p 命令查看飙高进程

top -p 15271           
[root~]# top -p 15271
top - 14:06:27 up 88 days,  4:29,  5 users,  load average: 4.14, 2.32, 1.32 
Tasks:   0 total,   0 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s): 90.2 us,  4.0 sy,  0.0 ni,  5.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st KiB
Mem :  8009264 total,  1529888 free,  4388024 used,  2091352 buff/cache
KiB Swap:  8388604 total,  8388604 free,        0 used.  3194448 avail Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND           

第三步:按H,获取每个线程的内存情况

需要注意的是,这里的H是大写的H。

JDK自带的调优工具

第四步:找到内存和cpu占用最高的线程tid

过上图我们看到占用cpu资源最高的线程有两个,线程号分别是4018362, 4018363。我们一第一个为 例说明,如何查询这个线程是哪个线程,以及这个线程的什么地方出现问题,导致cpu飙高。

第五步:将线程tid转化为十六进制

67187778是线程号为4013442的十六进制数。具体转换可以网上查询工具。

第六步:执行[ jstack 4018360 |grep -A 10 67187778] 查询飙高线

程的堆栈信息

接下来查询飙高线程的堆栈信息

jstack 4013440|grep -A 10 67190882
 
4013440:表示的是进程号
67187778:   表示的是线程号对应的十六进制数           

通过这个方式可以查询到这个线程对应的堆栈信息

JDK自带的调优工具

从这里我们可以看出有问题的线程id是0x4cd0, 哪一句代码有问题呢, Math类的22行。

第七步:查看对应的堆栈信息找出可能存在问题的代码

上述方法定位问题已经很精确了,接下来就是区代码里排查为什么会有问题了。

备注:上面的进程id可能没有对应上,在测试的时候,需要写对进程id和线程id

jvm调优通常使用的是Jstat命令

1. 垃圾回收统计 jstat -gc

jstat -gc 进程id           
JDK自带的调优工具
上面的参数分别是什么意思呢?先识别参数的含义,然后根据参数进行分析

S0C: 第一个Survivor区的容量
S1C: 第二个Survivor区的容量
S0U: 第一个Survivor区已经使用的容量
S1U:第二个Survivor区已经使用的容量
EC: 新生代Eden区的容量
EU: 新生代Eden区已经使用的容量
OC: 老年代容量
OU:老年代已经使用的容量
MC: 方法区大小 (元空间)
MU: 方法区已经使用的大小
CCSC:压缩指针占用空间
CCSU:压缩指针已经使用的空间
YGC: YoungGC已经发生的次数
YGCT: 这一次YoungGC耗时
FGC: Full GC发生的次数
FGCT: Full GC耗时
GCT: 总的GC耗时,等于YGCT+FGCT           

连续观察GC变化的命令

jstat -gc 进程ID 间隔时间  打印次数           
JDK自带的调优工具

这样就连续打印了10次gc的变化,每次隔一秒。

这个命令是对整体垃圾回收情况的统计,下面将会差分处理

2.堆内存统计

这个命令是打印堆内存的使用情况。

jstat -gccapacity 进程ID           
JDK自带的调优工具
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个Survivor区大小
S1C:第二个Survivor区大小
EC:Eden区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC: 当前老年代大小
MCMN: 最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数           

3.新生代垃圾回收统计

jstat -gcnew 进程ID [ 间隔时间  打印次数]           
JDK自带的调优工具
S0C:第一个Survivor的大小
S1C:第二个Survivor的大小
S0U:第一个Survivor已使用大小
S1U:第二个Survivor已使用大小
TT: 对象在新生代存活的次数
MTT: 对象在新生代存活的最大次数
DSS: 期望的Survivor大小
EC:Eden区的大小
EU:Eden区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间           

4. 新生代内存统计

jstat -gcnewcapacity 进程ID           
JDK自带的调优工具
参数含义:

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:Survivor 1区最大大小
S0C:当前Survivor 1区大小
S1CMX:Survivor 2区最大大小
S1C:当前Survivor 2区大小
ECMX:最大Eden区大小
EC:当前Eden区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数           

5. 老年代垃圾回收统计

jstat -gcold 进程ID           
JDK自带的调优工具
MC:方法区大小
MU:方法区已使用大小
CCSC:压缩指针类空间大小
CCSU:压缩类空间已使用大小
OC:老年代大小
OU:老年代已使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间,新生代+老年代           

6. 老年代内存统计

jstat -gcoldcapacity 进程ID           

7. 元数据空间统计

jstat -gcmetacapacity 进程ID           

8.整体运行情况

jstat -gcutil 进程ID           

继续阅读