我有个很大的疑问, 实际系统运行的过程中,会有很多接口,每个接口处理的逻辑又会不一样。 所以,每分钟产生的对象的大小是不一样的。 而且,系统的流量也是不一样的,有时候多,有时候少。 面对这种,内存中的垃圾是不稳定的状态, 有时候多, 有时候少, 而且不知道什么时候多。 也不知道什么时候会产生大对象。
这样的前提下,怎么进行 GC 调优呢?

应该按照最繁忙的时候,最大对象的情况来估算。 这样,就算最繁忙的时候,都只会进行少量的 Full GC 的话,那么空闲的时候也可以应对了。

可以使用原始的jdk提供的工具。也可以搭建JDK监控平台: Open-Falcon

如果执行jps 这些jdk的工具发生错误,提示找不到的话,可能是jdk版本安装错了。 需要安装devel 版本。可以用以下命令安装

  1. yum install java-1.8.0-openjdk-devel -y

通过 jstat 工具分析内存和GC

先执行 jps 找到当前java程序的PID

输出当前内存和GC的情况


[root@localhost ~]# jstat -gc 2949

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
1600.0 1600.0  0.0   801.8  13120.0   3862.0   32532.0    23618.1   57984.0 55429.6 7552.0 7078.9    299    0.653   3      0.187    0.840

比较重要的几个内容:

新生代增长率:
按照定时输出内存的使用情况。
jstat -gc PID 时间(毫秒) 输出次数

#每1秒输出PID=123的GC和内存情况,输出5次
jstat -gc 123 1000 5

这样就可以看到每秒内存的变动情况, 推测出新生代增长率

YGC频率和耗时:
可以根据GC日志来查看什么时候发生的YGC和耗时。
也可以根据jstat来看, 列的后面,有系统启动到执行命令的时候, 一共进行了多少次YGC,耗时多长。
YGC后有多少对象存活和进入老年代、老年代增长率:
没有直观的方式看得出来, 只能推测出来。
根据不同的情况去推测出来, YGC的频率,和FGC的频率。
Full GC频率和耗时:
仔细分析GC日志。
也可以根据jstat来看, 列的后面,有系统启动到执行命令的时候, 一共进行了多少次FGC,耗时多长。
然后分析推测。

通过jmap 工具分析对象的分布

用来输出堆空间的信息
jmap -heap PID


[root@localhost ~]# jmap -heap 2949
Attaching to process ID 2949, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.272-b10

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 257949696 (246.0MB)
   NewSize                  = 5570560 (5.3125MB)
   MaxNewSize               = 85983232 (82.0MB)
   OldSize                  = 11206656 (10.6875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 15073280 (14.375MB)
   used     = 6761720 (6.448478698730469MB)
   free     = 8311560 (7.926521301269531MB)
   44.85898225203805% used
Eden Space:
   capacity = 13434880 (12.8125MB)
   used     = 5940704 (5.665496826171875MB)
   free     = 7494176 (7.147003173828125MB)
   44.21851181402439% used
From Space:
   capacity = 1638400 (1.5625MB)
   used     = 821016 (0.7829818725585938MB)
   free     = 817384 (0.7795181274414062MB)
   50.11083984375% used
To Space:
   capacity = 1638400 (1.5625MB)
   used     = 0 (0.0MB)
   free     = 1638400 (1.5625MB)
   0.0% used
tenured generation:
   capacity = 33312768 (31.76953125MB)
   used     = 24184920 (23.064537048339844MB)
   free     = 9127848 (8.704994201660156MB)
   72.59955101899668% used

22018 interned Strings occupying 2259816 bytes.
[root@localhost ~]#

还有一个比较有用的用法,用来看看对象的分布情况。
#####jmap -histo PID

jmap -histo 2949

如果输出的内容太多,可以将内容保存到日志里面: 再看日志。

jmap -histo 2949 >a.log

和可以生成堆内存快照,然后使用jhat在浏览器中进行分析。

jmap -dump,format=b,file=dump.hprof PID

然后启动jhat在浏览器里面进行查看,可以指定端口,默认是7000


[root@localhost ~]# jhat dump.hprof
Reading from dump.hprof...
Dump file created Thu Nov 19 05:26:22 EST 2020
Snapshot read, resolving...
Resolving 385762 objects...
Chasing references, expect 77 dots.............................................................................
Eliminating duplicate references.............................................................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

然后直接浏览器访问地址+端口就可以了。

😨如何进行GC调优和问题定位 - 图1

还可以使用MAT进行更加细致的分析 。

分析大内存的时候, 例如32G 64G 。可以进去MAT的目录 , 执行 ./ParseHeapDump.sh来分析内存镜像, 然后得出报告, 导出报告到本地查看即可。

下面讲解一下MAT的报告怎么看: