【JVM学习】7.JVM调优


1 前言

本章将主要介绍调优工具及各自的使用方法,最后给出实战案例。

2 工具

2.1 JDK工具

假如你自己手动安装过JDK,那一定不会对下表中的工具感到陌生。比如用的最多的java

Windows下的JDK工具集合

不同平台,其对应的名字相差无几,只是后缀名不同。比如我自己的MAC OS版本:

MacOS下的JDK工具集合

在 Linux 中,一般自带了OpenJdk,一般情况下 JPS 等命令不能用,要么选择去安装 JPS 等插件,要么把 OpenJdk 卸载,去重新安装 Oracle 的 JDK,我推荐后者。

PS:本机JDK版本:1.8.0_181

2.1.1 jps

  • 作用:列出当前机器上正在运行的虚拟机进程。
  • 参数:
    • -q:仅显示进程
    • -m:输出主函数传入的参数
    • -l:输出应用程序主类完整 package 名称或 jar 完整名称
    • -v:列出 jvm 参数,如:-Xms1024m -Xmx1024m 是启动IDEA指定的 jvm 参数
      1892  -Xms1024m -Xmx1024m -XX:ReservedCodeCacheSize=512m -XX:+IgnoreUnrecognizedVMOptions -XX:+UseG1GC -XX:SoftRefLRUPolicyMSPerMB=50 -XX:CICompilerCount=2 -XX:+HeapDumpOnOutOfMemoryError

2.1.2 jstat

  • 作用:用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载内存垃圾收集JIT 编译等运行数据。是无GUI界面时的首选。
  • 参数:
    • -class:类加载统计。
      • Loaded:加载class的数量
      • Bytes:所占用空间大小
      • Unloaded:未加载数量
      • Bytes:未加载占用空间
      • Time:时间
    • -compiler:编译统计
      • Compiled:编译数量。
      • Failed:失败数量
      • Invalid:不可用数量
      • Time:时间
      • FailedType:失败类型
      • FailedMethod:失败的方法
    • -gc (GC 堆状态)
    • -gccapacity (各区大小)
    • -gccause (最近一次 GC 统计和原因)
    • -gcnew (新区统计)
    • -gcnewcapacity (新区大小)
    • -gcold (老区统计)
    • -gcoldcapacity (老区大小)
    • -gcpermcapacity (永久区大小)
    • -gcutil (GC 统计汇总)

比如:jstat -gc <pid>

[zyxelva@MacBook-Pro 15:58:19 ~/Documents/MyGit/JVM]$ jstat -gc 2085
 S0C       S1C       S0U    S1U      EC       EU            OC            OU       MC         MU        CCSC   CCSU   YGC  YGCT  FGC  FGCT     GCT   
10752.0 29184.0  0.0    0.0   476160.0 141011.1  167936.0   60198.8   98496.0 93078.6 13440.0 12512.2   16    0.825   4       0.809    1.634

各字段意义如下表所示:

字段含义
S0Csurvivor0区的总容量
S1Csurvivor1区的总容量
S0Usurvivor0区已使用的容量
S1Usurvivor1区已使用的容量
ECEden区的总容量
EUEden区已使用的容量
OCOld区的总容量
OUOld区已使用的容量
MCMetaspace的总容量
MUMetaspace已使用的容量
CCSC压缩类空间容量
CCSU压缩类空间已使用的容量
YGC新生代垃圾回收次数
YGCT新生代垃圾回收时间
FGCFull GC次数
FGCTFull GC时间
GCT垃圾回收总消耗时间

其他带参数命令的字段意义,可参考java高分局之jstat命令使用.

2.1.3 jmap

  • 作用:用于生成堆转储快照(一般称为 heapdumpdump 文件);查询 finalize 执行队列Java 堆永久代的详细信息,如空间使用率当前用的是哪种垃圾收集器等。
  • 参数:
    • -heap <pid>:打印 heap 的概要信息,GC 使用的算法,heap的配置及wise heap 的使用情况。
    • -histo:live <pid>:打印每个 class 的实例数目内存占用类全名信息;如果 live 子参数加上后,只统计活的对象数量。
    • -dump <pid>:生成的堆转储快照。一般这么用:jmap -dump:live,format=b,file=heap.bin <pid>.

PS:jmap 有不少功能在 Windows 平台下都是受限的,除了生成 dump 文件的 -dump 选项和用于查看每个类的实例、空间占用统计的-histo 选项在所有操作系统都提供之外,其余选项都只能在 Linux/Solaris 下使用。
本机系统为macOS Catalina 10.15.7,玩不了这个命令,据百度、Google说,要升级到至少9才能用。我特么……

借用别人的机子来解释下各个字段的意思。

jmap-heap

例:jmap -heap 15756

Heap Configuration: ##堆配置情况,也就是 JVM 参数配置的结果[平常说的 tomcat 配置 JVM 参数,就是在配置这些] 
    MinHeapFreeRatio = 40 ##最小堆使用比例
    MaxHeapFreeRatio = 70 ##最大堆可用比例      
    MaxHeapSize = 2147483648 (2048.0MB) ##最大堆空间大小
    NewSize = 268435456 (256.0MB) ##新生代分配大小 
    MaxNewSize = 268435456 (256.0MB) ##最大可新生代分配大小
    OldSize = 5439488 (5.1875MB) ##老年代大小 
    NewRatio = 2 ##新生代比例
    SurvivorRatio  = 8 ##新生代与 suvivor 的比例 
    PermSize = 134217728 (128.0MB) ##perm区永久代大小
    MaxPermSize = 134217728 (128.0MB) ##最大可分配 perm 区,也就是永久代大小      

Heap Usage: ##堆使用情况【堆内存实际的使用情况】
  New Generation (Eden + 1 Survivor Space): ##新生代(伊甸区 Eden 区 + 幸存区 survior(1+2)空间) 
      capacity = 241631232 (230.4375MB) ##伊甸区容量
      used = 77776272 (74.17323303222656MB) ##已经使用大小 
      free = 163854960 (156.26426696777344MB) ##剩余容量 
      32.188004570534986% used ##使用比例
  Eden Space: ##伊甸区 
      capacity = 214827008 (204.875MB) ##伊甸区容量
      used = 74442288 (70.99369812011719MB) ##伊甸区使用
      free = 140384720 (133.8813018798828MB) ##伊甸区当前剩余容量 
      34.65220164496263% used ##伊甸区使用情况 
  From Space: ##survior1 区 
      capacity = 26804224 (25.5625MB) ##survior1 区容量
      used = 3333984 (3.179534912109375MB) ##surviror1 区已使用情况
      free = 23470240 (22.382965087890625MB) ##surviror1 区剩余容量 
      12.43827838477995% used ##survior1 区使用比例
  To Space: ##survior2 区 
      capacity = 26804224 (25.5625MB) ##survior2 区容量
      used = 0 (0.0MB) ##survior2 区已使用情况
      free = 26804224 (25.5625MB) ##survior2 区剩余容量 
      0.0% used ## survior2 区使用比例
  PS Old Generation: ##老年代使用情况 
      capacity = 1879048192 (1792.0MB) ##老年代容量
      used = 30847928 (29.41887664794922MB) ##老年代已使用容量 
      free = 1848200264 (1762.5811233520508MB) ##老年代剩余容量 
      1.6416783843721663% used ##老年代使用比例

2.1.4 jhat

  • 作用:分析 jmap 生成的 dump
  • 参数:

常和jmap配合,命令jhat [options] <heap-dump-file-name>,比如:利用jmap生成了文件名为heapDumpdump日志,

HollisMacBook-Air:apaas hollis$ jhat heapDump
Reading from heapDump...
Dump file created Thu Jan 21 18:59:51 CST 2016
Snapshot read, resolving...
Resolving 341297 objects...
Chasing references, expect 68 dots....................................................................
Eliminating duplicate references....................................................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

使用jhat命令,就启动了一个http服务,端口是7000,然后在访问http://localhost:7000/

jhat界面

2.1.5 jstack

  • 作用:用于生成虚拟机当前时刻的线程快照。
  • 参数:

线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。一般来说 jstack 主要是用来排查是否有死锁的情况

2.1.6 jinfo

  • 作用:实时查看和调整虚拟机运行参数。
  • 参数:
    • –sysprops <pid>:可以查看由System.getProperties()取得的参数
    • –flag <pid>:未被显式指定的参数的系统默认值
    • –flags <pid>:注意 s,显示虚拟机的参数

2.1.7 命令行工具总结

  • 生产服务器推荐开启:
    • -XX:-HeapDumpOnOutOfMemoryError:默认关闭,建议开启,在 java.lang.OutOfMemoryError 异常出现时,输出一个 dump 文件,记录当时的堆内存快照。
    • -XX:HeapDumpPath=./java_pid<pid>.hprof:用来设置堆内存快照的存储文件路径,默认是java进程启动位置。
  • 调优之前开启、调优之后关闭:
    • -XX:+PrintGC:调试跟踪之打印简单的 GC 信息参数
    • -XX:+PrintGCDetails, +XX:+PrintGCTimeStamps:打印详细的 GC 信息
    • -Xlogger:logpath:设置 gc 的日志路,如: -Xlogger:log/gc.log, 将 gc.log 的路径设置到当前目录的 log 目录下。将 GC 的日志独立写入日志文件,将 GC 日志与系统业务日志进行了分离,方便开发人员进行追踪分析。
  • 考虑使用:
    • -XX:+PrintHeapAtGC:打印推信息,获取 Heap 在每次垃圾回收前后的使用状况。
    • -XX:+TraceClassLoading:在系统控制台信息中看到 class 加载的过程和具体的 class 信息,可用以分析类的加载顺序以及是否可进行精简操作。
    • -XX:+DisableExplicitGC:禁止在运行期显式地调用System.gc()

2.2 可视化工具

2.2.1 Jconsole

“A JMX-compliant graphical tool for monitoring a Java virtual machine. It can monitor both local and remote JVMs. It can also monitor and manage an application.”
意思很直白,就是一个JMX编译而成的监控JVM的图形化工具

举例:先启动一个Java应用,我这里启动了一个Eureka注册中心,单节点。然后找到Java安装目录下的bin目录,双击jconsole,默认会打开新建连接页面,选中刚刚启动的Eureka即可,界面如下:

jconsole界面

通过切换tab,可以查看你所需要的东西,比如
内存:

jconsole界面之内存

线程:

jconsole界面之线程

类:

jconsole界面之类

VM概要:

jconsole界面之线程

2.2.2 jvisualvm

启动方式同上。

jvisualvm界面

一般来说,这个工具是本机调试用,一般生产上来说,你一般是用不了的(除非启用远程连接)。

监视:

jvisualvm界面之监视

其中,堆dump类似于jmap

线程:

jvisualvm界面之线程

其中,线程Dump类似于jstack工具中的栈跟踪。

抽样器:

jvisualvm界面之抽样器

profiler:

jvisualvm界面之profiler

2.3 Arthas

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。

2.3.1 安装

参见官网文档.

本机采用手动下载jar包和启动文件as.sh方式。

[zyxelva@172-104-123-96 10:56:11 ~/Documents/Arthas]$ ./as.sh
Arthas script version: 3.6.9
[INFO] JAVA_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home
Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 1219 com.taeyeon.adminclient.AdminServerApplication
  [2]: 2196 org.jetbrains.jps.cmdline.Launcher
  [3]: 1149
  [4]: 1181 com.taeyeon.eureka.SpringCloudEurekaApplication
4
Arthas home: /Users/zyxelva/.arthas/lib/3.6.9/arthas
Calculating attach execution time...
Attaching to 1181 using version /Users/zyxelva/.arthas/lib/3.6.9/arthas...

real	0m1.963s
user	0m0.399s
sys	0m0.049s
Attach success.
telnet connecting to arthas server... current timestamp is 1689044193
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'

wiki       https://arthas.aliyun.com/doc
tutorials  https://arthas.aliyun.com/doc/arthas-tutorials.html
version    3.6.9
main_class com.taeyeon.eureka.SpringCloudEurekaApplication
pid        1181
time       2023-07-11 10:56:32

[arthas@1181]$

启动后,首先会让你选择attach一个进程,这里同样监控eureka实例,即pid1181的那个。接下来通过介绍各个命令参数来使用arthas。

如果想在浏览器使用命令玩转arthas,也可用在启动后,通过访问http://127.0.0.1:8563/,使用Web Console。也可以填入 IP,远程连接其它机器上的 arthas。

Web Console

2.3.2 入门

2.3.2.1 dashboard

输入dashboard,按回车/enter,会展示当前attach进程的信息,按ctrl+c可以中断执行。

dashboard

当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数, 线程池信息等等。

更多详情,可参阅dashboard官方文档.

2.3.2.2 Thread

这个命令和 jstack 很相似,但是功能更加强大,主要是查看当前 JVM 的线程堆栈信息,同时可以结合使用 thread –b 来进行死锁的排查

参数解释:

  • -n:指定最忙的前 n 个线程并打印堆栈
  • -b:找出阻塞当前线程的线程
  • -i:指定 cpu 占比统计的采样间隔,单位为毫秒

例1:查看当前最忙的前 N 个线程并打印堆栈:

$ thread -n 3
"C1 CompilerThread0" [Internal] cpuUsage=1.63% deltaTime=3ms time=1170ms


"arthas-command-execute" Id=23 cpuUsage=0.11% deltaTime=0ms time=401ms RUNNABLE
    at java.management@11.0.7/sun.management.ThreadImpl.dumpThreads0(Native Method)
    at java.management@11.0.7/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:199)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
    at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
    at java.base@11.0.7/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base@11.0.7/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base@11.0.7/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
    at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base@11.0.7/java.lang.Thread.run(Thread.java:834)


"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=584ms

例2:当没有参数时,显示第一页线程的信息:

[arthas@1181]$ thread
Threads Total: 65, NEW: 0, RUNNABLE: 25, BLOCKED: 0, WAITING: 19, TIMED_WAITING: 11, TERMINATED: 0, Internal threads: 10              
ID    NAME                             GROUP            PRIORITY   STATE      %CPU       DELTA_TIME  TIME       INTERRUPTE DAEMON     
-1    C1 CompilerThread2               -                -1         -          1.16       0.002       0:8.016    false      true       
130   arthas-command-execute           system           5          RUNNABLE   0.47       0.000       0:0.024    false      true       
54    Eureka-CacheFillTimer            main             5          TIMED_WAIT 0.1        0.000       0:0.244    false      true       
-1    VM Periodic Task Thread          -                -1         -          0.08       0.000       0:6.228    false      true       
42    Catalina-utility-2               main             1          TIMED_WAIT 0.05       0.000       0:0.843    false      false      
-1    C2 CompilerThread1               -                -1         -          0.05       0.000       0:0.235    false      true       
-1    C2 CompilerThread0               -                -1         -          0.02       0.000       0:0.238    false      true       
41    Catalina-utility-1               main             1          WAITING    0.02       0.000       0:0.864    false      false      
2     Reference Handler                system           10         WAITING    0.0        0.000       0:0.293    false      true       
3     Finalizer                        system           8          WAITING    0.0        0.000       0:0.031    false      true       
4     Signal Dispatcher                system           9          RUNNABLE   0.0        0.000       0:0.000    false      true       
13    RMI TCP Accept-0                 system           5          RUNNABLE   0.0        0.000       0:0.054    false      true       
14    Attach Listener                  system           9          RUNNABLE   0.0        0.000       0:0.886    false      true       
16    RMI Scheduler(0)                 system           5          WAITING    0.0        0.000       0:0.011    false      true       
106   arthas-timer                     system           9          WAITING    0.0        0.000       0:0.000    false      true       
109   arthas-NettyHttpTelnetBootstrap- system           5          RUNNABLE   0.0        0.000       0:0.029    false      true       
110   arthas-NettyWebsocketTtyBootstra system           5          RUNNABLE   0.0        0.000       0:0.018    false      true       
111   arthas-NettyWebsocketTtyBootstra system           5          RUNNABLE   0.0        0.000       0:0.002    false      true       
112   arthas-shell-server              system           9          TIMED_WAIT 0.0        0.000       0:0.008    false      true       
113   arthas-session-manager           system           9          TIMED_WAIT 0.0        0.000       0:0.004    false      true       
114   arthas-UserStat                  system           9          WAITING    0.0        0.000       0:0.000    false      true       
116   arthas-NettyHttpTelnetBootstrap- system           5          RUNNABLE   0.0        0.000       0:0.084    false      true       
117   arthas-NettyWebsocketTtyBootstra system           5          RUNNABLE   0.0        0.000       0:0.106    false      true       
118   arthas-TermServer-1-1            system           5          RUNNABLE   0.0        0.000       0:0.004    false      true       
119   arthas-NettyWebsocketTtyBootstra system           5          RUNNABLE   0.0        0.000       0:0.009    false      true       
120   arthas-TermServer-1-2            system           5          RUNNABLE   0.0        0.000       0:0.011    false      true       
121   arthas-NettyWebsocketTtyBootstra system           5          RUNNABLE   0.0        0.000       0:0.007    false      true       
122   arthas-NettyWebsocketTtyBootstra system           5          RUNNABLE   0.0        0.000       0:0.009    false      true       
123   arthas-TermServer-1-3            system           5          RUNNABLE   0.0        0.000       0:0.003    false      true       
124   arthas-NettyWebsocketTtyBootstra system           5          RUNNABLE   0.0        0.000       0:0.008    false      true       
[arthas@1181]$ 

更多介绍,参阅thread官方文档.

2.3.2.3 jvm

查看当前 JVM 信息.

[arthas@1181]$ jvm
 RUNTIME                                                                                                                              
--------------------------------------------------------------------------------------------------------------------------------------
 MACHINE-NAME                          1181@172-104-123-96.ip.linodeusercontent.com                                                   
 JVM-START-TIME                        2023-07-11 09:27:13                                                                            
 MANAGEMENT-SPEC-VERSION               1.2                                                                                            
 SPEC-NAME                             Java Virtual Machine Specification                                                             
 SPEC-VENDOR                           Oracle Corporation                                                                             
 SPEC-VERSION                          1.8                                                                                            
 VM-NAME                               Java HotSpot(TM) 64-Bit Server VM                                                              
 VM-VENDOR                             Oracle Corporation                                                                             
 VM-VERSION                            25.181-b13                                                                                     
 INPUT-ARGUMENTS                       -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:50075,suspend=y,server=n                  
                                       -XX:TieredStopAtLevel=1                                                                        
                                       -Xverify:none                                                                                  
                                       -Dspring.output.ansi.enabled=always                                                            
                                       -javaagent:/Users/zyxelva/Library/Caches/JetBrains/IntelliJIdea2021.3/captureAgent/debugger-ag 
                                       ent.jar                                                                                        
                                       -Dcom.sun.management.jmxremote                                                                 
                                       -Dspring.jmx.enabled=true                                                                      
                                       -Dspring.liveBeansView.mbeanDomain                                                             
                                       -Dspring.application.admin.enabled=true                                                        
                                       -Dfile.encoding=UTF-8                                                                          
 CLASS-PATH                            /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/charsets.jar:/Library 
                                       /Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/Java 
                                       VirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirt 
                                       ualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMach 
                                       ines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/ 
                                       jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8. 
                                       0_181.jdk/Contents/Home/jre/lib/ext/localedata.jar:此处省略一万字符                                                                         
 BOOT-CLASS-PATH                       /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/resources.jar:/Librar 
                                       y/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVir 
                                       tualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMa 
                                       chines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1. 
                                       8.0_181.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/C 
                                       ontents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/ 
                                       Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/clas 
                                       ses:/Users/zyxelva/Library/Caches/JetBrains/IntelliJIdea2021.3/captureAgent/debugger-agent.jar 
 LIBRARY-PATH                          /Users/zyxelva/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensio 
                                       ns:/System/Library/Java/Extensions:/usr/lib/java:.                                             
                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 CLASS-LOADING                                                                                                                        
--------------------------------------------------------------------------------------------------------------------------------------
 LOADED-CLASS-COUNT                    30462                                                                                          
 TOTAL-LOADED-CLASS-COUNT              30485                                                                                          
 UNLOADED-CLASS-COUNT                  23                                                                                             
 IS-VERBOSE                            false                                                                                          
                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 COMPILATION                                                                                                                          
--------------------------------------------------------------------------------------------------------------------------------------
 NAME                                  HotSpot 64-Bit Tiered Compilers                                                                
 TOTAL-COMPILE-TIME                    23630                                                                                          
 [time (ms)]                                                                                                                          
                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 GARBAGE-COLLECTORS                                                                                                                   
--------------------------------------------------------------------------------------------------------------------------------------
 PS Scavenge                           name : PS Scavenge                                                                             
 [count/time (ms)]                     collectionCount : 25                                                                           
                                       collectionTime : 984                                                                           
 PS MarkSweep                          name : PS MarkSweep                                                                            
 [count/time (ms)]                     collectionCount : 12                                                                           
                                       collectionTime : 2333                                                                          
                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 MEMORY-MANAGERS                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 CodeCacheManager                      Code Cache                                                                                     
 Metaspace Manager                     Metaspace                                                                                      
                                       Compressed Class Space                                                                         
 PS Scavenge                           PS Eden Space                                                                                  
                                       PS Survivor Space                                                                              
 PS MarkSweep                          PS Eden Space                                                                                  
                                       PS Survivor Space                                                                              
                                       PS Old Gen                                                                                     
                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 MEMORY                                                                                                                               
--------------------------------------------------------------------------------------------------------------------------------------
 HEAP-MEMORY-USAGE                     init : 134217728(128.0 MiB)                                                                    
 [memory in bytes]                     used : 277785000(264.9 MiB)                                                                    
                                       committed : 1059061760(1010.0 MiB)                                                             
                                       max : 1908932608(1.8 GiB)                                                                      
 NO-HEAP-MEMORY-USAGE                  init : 2555904(2.4 MiB)                                                                        
 [memory in bytes]                     used : 198825896(189.6 MiB)                                                                    
                                       committed : 208535552(198.9 MiB)                                                               
                                       max : -1(-1 B)                                                                                 
 PENDING-FINALIZE-COUNT                0                                                                                              
                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 OPERATING-SYSTEM                                                                                                                     
--------------------------------------------------------------------------------------------------------------------------------------
 OS                                    Mac OS X                                                                                       
 ARCH                                  x86_64                                                                                         
 PROCESSORS-COUNT                      4                                                                                              
 LOAD-AVERAGE                          2.37109375                                                                                     
 VERSION                               10.15.7                                                                                        
                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 THREAD                                                                                                                               
--------------------------------------------------------------------------------------------------------------------------------------
 COUNT                                 55                                                                                             
 DAEMON-COUNT                          51                                                                                             
 PEAK-COUNT                            56                                                                                             
 STARTED-COUNT                         120                                                                                            
 DEADLOCK-COUNT                        0                                                                                              
                                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 FILE-DESCRIPTOR                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------------
 MAX-FILE-DESCRIPTOR-COUNT             10240                                                                                          
 OPEN-FILE-DESCRIPTOR-COUNT            273                                                                                            
                                           

2.3.2.4 Jad

反编译指定已加载类的源码。

/**
[arthas@1181]$ jad com.taeyeon.eureka.SpringCloudEurekaApplication

ClassLoader:                                                                                                                          
 +-sun.misc.Launcher$AppClassLoader@18b4aac2
 +-sun.misc.Launcher$ExtClassLoader@1df82230                                                                                         

Location:                                                                                                                             
/Users/zyxelva/Documents/xxx/xxx/spring-boot-interview-eureka/target/classes/                                                 
*/
       /*
        * Decompiled with CFR.
        */
       package com.taeyeon.eureka;
       
       import org.slf4j.Logger;
       import org.slf4j.LoggerFactory;
       import org.springframework.boot.SpringApplication;
       import org.springframework.boot.autoconfigure.SpringBootApplication;
       import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
       
       @SpringBootApplication
       @EnableEurekaServer
       public class SpringCloudEurekaApplication {
           private static final Logger log = LoggerFactory.getLogger(SpringCloudEurekaApplication.class);
       
           public static void main(String[] args) {
/*18*/         log.info("------------------------SpringCloudEurekaApplication 开始启动------------------------");
/*19*/         SpringApplication.run(SpringCloudEurekaApplication.class, args);
/*20*/         log.info("------------------------SpringCloudEurekaApplication 启动成功------------------------");
           }
       }

//Affect(row-cnt:2) cost in 1454 ms.

2.3.3 命令汇总

命令介绍
dashboard当前系统的实时数据面板
thread查看当前 JVM 的线程堆栈信息
watch方法执行数据观测
trace方法内部调用路径,并输出方法路径上的每个节点上耗时
stack输出当前方法被调用的调用路径
tt方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
monitor方法执行监控
jvm查看当前 JVM 信息
vmoption查看,更新 JVM 诊断相关的参数
sc查看 JVM 已加载的类信息
sm查看已加载类的方法信息
jad反编译指定已加载类的源码
classloader查看 classloader 的继承树,urls,类加载信息
heapdump类似 jmap 命令的 heap dump 功能

更多命令使用,参阅命令列表.

3 调优

3.1 内存优化

3.1.1 AB压测

压测工具AB(Apache Bench)测试工具是 Apache 提供的一款测试工具,具有简单易上手的特点,在测试 Web 服务时非常实用。

ab 一般都是在 Linux 上用。安装非常简单,只需要在 Linux 系统中输入 yum -y install httpd-tools 命令,就可以了。

本机macOS自带了,所以,怎么安装,请自行Google。

安装成功后,输入 ab 命令,可以看到以下信息:

[zyxelva@Taeyeons-MacBook-Pro 09:58:45 ~]$ ab
ab: wrong number of arguments
Usage: ab [options] [http[s]://]hostname[:port]/path
Options are:
    -n requests     Number of requests to perform
    -c concurrency  Number of multiple requests to make at a time
    -t timelimit    Seconds to max. to spend on benchmarking
                    This implies -n 50000
    -s timeout      Seconds to max. wait for each response
                    Default is 30 seconds
    -b windowsize   Size of TCP send/receive buffer, in bytes
    -B address      Address to bind to when making outgoing connections
    -p postfile     File containing data to POST. Remember also to set -T
    -u putfile      File containing data to PUT. Remember also to set -T
    -T content-type Content-type header to use for POST/PUT data, eg.
                    'application/x-www-form-urlencoded'
                    Default is 'text/plain'
    -v verbosity    How much troubleshooting info to print
    -w              Print out results in HTML tables
    -i              Use HEAD instead of GET
    -x attributes   String to insert as table attributes
    -y attributes   String to insert as tr attributes
    -z attributes   String to insert as td or th attributes
    -C attribute    Add cookie, eg. 'Apache=1234'. (repeatable)
    -H attribute    Add Arbitrary header line, eg. 'Accept-Encoding: gzip'
                    Inserted after all normal header lines. (repeatable)
    -A attribute    Add Basic WWW Authentication, the attributes
                    are a colon separated username and password.
    -P attribute    Add Basic Proxy Authentication, the attributes
                    are a colon separated username and password.
    -X proxy:port   Proxyserver and port number to use
    -V              Print version number and exit
    -k              Use HTTP KeepAlive feature
    -d              Do not show percentiles served table.
    -S              Do not show confidence estimators and warnings.
    -q              Do not show progress when doing more than 150 requests
    -l              Accept variable document length (use this for dynamic pages)
    -g filename     Output collected data to gnuplot format file.
    -e filename     Output CSV file with percentages served
    -r              Don't exit on socket receive errors.
    -m method       Method name
    -h              Display usage information (this message)
    -I              Disable TLS Server Name Indication (SNI) extension
    -Z ciphersuite  Specify SSL/TLS cipher suite (See openssl ciphers)
    -f protocol     Specify SSL/TLS protocol
                    (TLS1, TLS1.1, TLS1.2 or ALL)
    -E certfile     Specify optional client certificate chain and private key
  • 测试Get请求:
    ab -c 10 -n 100 http://www.test.api.com/test/login?userName=test&password=test
  • 测试Post请求:
    ab -n 100 -c 10 -p 'post.txt' -T 'application/x-www-form-urlencoded' 'http://test.api.com/test/register'
    其中,post.txt存放了该接口的参数,格式为:usernanme=test&password=test&sex=1
  • 参数的含义:
    • -n:总请求次数(最小默认为 1);
    • -c:并发次数(最小默认为 1 且不能大于总请求次数,例如:10 个请求,10 个并发,实际就是 1 人请求 1 次);
    • -p:post 参数文档路径(-p-T参数要配合使用);
    • -T:header 头内容类型(此处切记是大写英文字母 T);
  • 性能指标参考:
    • Requests per second:吞吐率,指某个并发用户数下单位时间内处理的请求数;
    • Time per request:上面的是用户平均请求等待时间,指处理完成所有请求数所花费的时间 /(总请求数 / 并发用户数);
    • Time per request:下面的是服务器平均请求处理时间,指处理完成所有请求数所花费的时间 / 总请求数;
    • Percentage of the requests served within a certain time:每秒请求时间分布情况,指在整个请求中,每个请求的时间长度的分布情况,例如有 50% 的请求 响应在 8ms 内,66% 的请求响应在 10ms 内,说明有 16% 的请求在 8ms~10ms 之间。

3.1.2 JVM 内存分配的调优案例

本人的老苹果快8年了,扛不住10万的并发了,这里只是抛砖引玉,模拟一万并发,各位看官不要唏嘘mac的性能哈。

3.1.2.1 测试接口

以下代码为待压测的接口:

@RequestMapping(value = "/neo")
@RestController
public class ApiController {
  @GetMapping("/heap")
  public String test() {
    List<Byte[]> list = new ArrayList<Byte[]>();
    Byte[] b = new Byte[1024 * 1024];
    list.add(b);
    return "success";
  }
}

3.1.2.2 样例

对应用服务进行压力测试,模拟不同并发用户数下的服务的响应情况:

  • 10 个并发用户/1万请求量(总):ab -c 10 -n 10000 http://127.0.0.1:8088/neo/heap
  • 100 个并发用户/1万请求量(总):ab -c 100 -n 10000 http://127.0.0.1:8088/neo/heap
  • 1000 个并发用户/1万请求量(总):ab -c 1000 -n 10000 http://127.0.0.1:8088/neo/heap

3.1.2.3 监控

  • GC 监控
    jstat -gc 1816 5000 20 | awk '{print $13,$14,$15,$16,$17}'
    其中,1816为应用进程ID。
    [zyxelva@Taeyeons-MacBook-Pro 10:28:15 ~]$ jps
    1816 spring-boot-interview-api-gateway.jar
    1755 Jps
  • 堆监控
    在默认不配置 JVM 堆内存大小的情况下,JVM 根据默认值来配置当前内存大小。 我们可以通过以下命令来查看堆内存配置的默认值:
    [zyxelva@Taeyeons-MacBook-Pro 10:30:12 ~]$ java -XX:+PrintFlagsFinal -version | grep HeapSize
      uintx ErgoHeapSizeLimit                         = 0                                   {product}
      uintx HeapSizePerGCThread                       = 87241520                            {product}
      uintx InitialHeapSize                          := 134217728                           {product}
      uintx LargePageHeapSizeThreshold                = 134217728                           {product}
      uintx MaxHeapSize                              := 2147483648                          {product}
    java version "1.8.0_181"
    Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
    Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
    可以看出,这台机器上启动的 JVM 默认最大堆内存大约为 2GB,初始化大小大约为 130MB。

3.1.2.4 压测结果

3.1.2.4.1 10 个并发用户/1万请求量(总)
[zyxelva@Taeyeons-MacBook-Pro 10:43:36 ~]$ ab -c 10 -n 10000 http://127.0.0.1:8088/neo/heap
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8088

Document Path:          /neo/heap
Document Length:        7 bytes

Concurrency Level:      10
Time taken for tests:   14.078 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1390000 bytes
HTML transferred:       70000 bytes
Requests per second:    710.30 [#/sec] (mean)
Time per request:       14.078 [ms] (mean)
Time per request:       1.408 [ms] (mean, across all concurrent requests)
Transfer rate:          96.42 [Kbytes/sec] received

Connection Times (ms)
min  mean[+/-sd] median   max
Connect:        0    4   4.3      4     271
Processing:     2    9  13.5      8     390
Waiting:        0    7  13.0      6     390
Total:          3   13  13.9     12     390

Percentage of the requests served within a certain time (ms)
50%     12
66%     13
75%     14
80%     15
90%     16
95%     19
98%     25
99%     39
100%    390 (longest request)

GC情况:

[zyxelva@172-104-123-96 10:47:54 ~]$ jstat -gc 1816 5000 20 | awk '{print $13,$14,$15,$16,$17 }'
YGC YGCT FGC FGCT GCT
79 0.625 3 0.287 0.912
80 0.625 3 0.287 0.912
111 0.890 3 0.287 1.177
139 1.100 3 0.287 1.387
141 1.119 3 0.287 1.406
141 1.119 3 0.287 1.406
141 1.119 3 0.287 1.406
141 1.119 3 0.287 1.406
141 1.119 3 0.287 1.406
141 1.119 3 0.287 1.406
141 1.119 3 0.287 1.406
141 1.119 3 0.287 1.406

测试结果显示:

  • 用户的吞吐量大于在710.3/秒左右
  • JVM服务器平均请求处理时间1.408ms左右
  • JVM服务器发生了141次YGC,耗时1.119秒,还有3次FGC,0.287秒左右,加在一起GC耗时1.406秒.
3.1.2.4.2 100 个并发用户/1万请求量(总)
[zyxelva@Taeyeons-MacBook-Pro 10:49:49 ~]$ ab -c 100 -n 10000 http://127.0.0.1:8088/neo/heap
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8088

Document Path:          /neo/heap
Document Length:        7 bytes

Concurrency Level:      100
Time taken for tests:   10.264 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1390000 bytes
HTML transferred:       70000 bytes
Requests per second:    974.30 [#/sec] (mean)
Time per request:       102.638 [ms] (mean)
Time per request:       1.026 [ms] (mean, across all concurrent requests)
Transfer rate:          132.25 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   36  18.4     37     194
Processing:     4   66  22.7     63     233
Waiting:        1   47  19.9     46     208
Total:          6  102  29.8    104     289

Percentage of the requests served within a certain time (ms)
  50%    104
  66%    113
  75%    118
  80%    120
  90%    130
  95%    144
  98%    161
  99%    212
 100%    289 (longest request)

GC情况:

[zyxelva@172-104-123-96 10:49:41 ~]$ jstat -gc 1816 5000 20 | awk '{print $13,$14,$15,$16,$17 }'
YGC YGCT FGC FGCT GCT
79        1.149   6       0.553    1.702
79        1.149   6       0.553    1.702
79        1.149   6       0.553    1.702
97        1.282   6       0.553    1.834
123      1.459   6       0.553    2.011
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119
142      1.567   6       0.553    2.119

测试结果显示:

  • 用户的吞吐量大于在974.30/秒左右
  • JVM服务器平均请求处理时间1.026ms左右
  • JVM服务器发生了142多次YGC,耗时1.567秒,还有6次FGC,0.553秒左右,加在一起GC耗时2.12秒.

#####3.1.2.4.3 1000 个并发用户/1万请求量(总)

[zyxelva@Taeyeons-MacBook-Pro 10:49:49 ~]$ ab -c 1000 -n 10000 http://127.0.0.1:8088/neo/heap
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8088

Document Path:          /neo/heap
Document Length:        7 bytes

Concurrency Level:      1000
Time taken for tests:   8.364 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1390000 bytes
HTML transferred:       70000 bytes
Requests per second:    1262.37 [#/sec] (mean)
Time per request:       78.638 [ms] (mean)
Time per request:       0.786 [ms] (mean, across all concurrent requests)
Transfer rate:          169.25 [Kbytes/sec] received

Connection Times (ms)
                    min  mean[+/-sd] median   max
Connect:        0    1    0.6      1       8
Processing:    1    78  85.0    53     1006
Waiting:         1   77   84.8    52     1005
Total:             1   79   85.0    54     1006

Percentage of the requests served within a certain time (ms)
  50%    54
  66%    69
  75%    83
  80%    95
  90%    152
  95%    260
  98%    386
  99%    458
 100%   1006 (longest request)
3.1.2.4.4 结果分析
  • GC 频率:高频的Full GC会给系统带来非常大的性能消耗,虽然Minor GC相对Full GC来说好了许多,但过多的Minor GC仍会给系统带来压力。
  • 内存:这里的内存指的是堆内存大小,堆内存又分为年轻代内存老年代内存。堆内存不足,会增加Minor GC,影响系统性能。
  • 吞吐量:频繁的 GC 将会引起线程的上下文切换,增加系统的性能开销,从而影响每次处理的线程请求,最终导致系统的吞吐量下降。
  • 延时:JVM 的 GC 持续时间也会影响到每次请求的响应时间。

3.1.3 调优方案

3.1.3.1 调整方案一

调整堆内存空间减少GC,通过分析,堆内存基本被用完了,而且存在大量Minor GCFull GC,这意味着我们的堆内存严重不足,这个时候我们需要调大堆内存空间。

# 堆空间加大到 3.0G
java -jar -Xms3000m -Xmx3000m spring-boot-interview-api-gateway.jar
3.1.3.1.1 10 个并发用户/1万请求量(总)
[zyxelva@172-104-123-96 14:43:53 ~]$ ab -c 10 -n 10000 http://127.0.0.1:8088/neo/heap
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8088

Document Path:          /neo/heap
Document Length:        7 bytes

Concurrency Level:      10
Time taken for tests:   10.096 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1390000 bytes
HTML transferred:       70000 bytes
Requests per second:    990.53 [#/sec] (mean)
Time per request:       10.096 [ms] (mean)
Time per request:       1.010 [ms] (mean, across all concurrent requests)
Transfer rate:          134.46 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3   3.4      3     213
Processing:     1    6   6.3      6     217
Waiting:        0    5   4.0      4     213
Total:          2   10   7.3      9     222

Percentage of the requests served within a certain time (ms)
  50%      9
  66%     10
  75%     11
  80%     12
  90%     13
  95%     14
  98%     16
  99%     18
 100%    222 (longest request)

GC情况:

[zyxelva@172-104-123-96 14:43:48 ~]$ jstat -gc 3757 5000 20 | awk '{print $13,$14,$15,$16,$17 }'
YGC YGCT FGC FGCT GCT
131 1.011 2 0.111 1.122
139 1.049 2 0.111 1.160
161 1.152 2 0.111 1.263
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330
173 1.219 2 0.111 1.330

测试结果显示:

  • 用户的吞吐量大于在990.53/秒左右
  • JVM服务器平均请求处理时间1.010ms左右
  • JVM服务器发生了173次YGC,耗时1.219秒,还有2次FGC,0.111秒左右,加在一起GC耗时1.33秒.
3.1.3.1.2 100 个并发用户/1万请求量(总)
[zyxelva@172-104-123-96 14:19:07 ~]$ ab -c 100 -n 10000 http://127.0.0.1:8088/neo/heap
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8088

Document Path:          /neo/heap
Document Length:        7 bytes

Concurrency Level:      100
Time taken for tests:   13.893 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1390000 bytes
HTML transferred:       70000 bytes
Requests per second:    719.80 [#/sec] (mean)
Time per request:       138.927 [ms] (mean)
Time per request:       1.389 [ms] (mean, across all concurrent requests)
Transfer rate:          97.71 [Kbytes/sec] received

Connection Times (ms)
                    min  mean[+/-sd] median   max
Connect:        0   44  24.3     46     211
Processing:    4   93  45.9     84     479
Waiting:        1   67  43.6     60     477
Total:            5  137  44.5    130     481

Percentage of the requests served within a certain time (ms)
  50%    130
  66%    142
  75%    150
  80%    154
  90%    176
  95%    200
  98%    282
  99%    348
 100%    481 (longest request)

GC情况:

[zyxelva@172-104-123-96 14:20:18 ~]$ jstat -gc 3757 5000 20 | awk '{print $13,$14,$15,$16,$17 }'
YGC YGCT FGC FGCT GCT
2           0.026   2     0.111 0.137
2           0.026   2     0.111 0.137
20         0.436   2     0.111 0.547
37         0.568   2     0.111 0.679
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733
47         0.622   2     0.111 0.733

测试结果显示:

  • 用户的吞吐量大于在719.8/秒左右
  • JVM服务器平均请求处理时间1.389ms左右
  • JVM服务器发生了47多次YGC,耗时1.622秒,还有2次FGC,0.111秒左右,加在一起GC耗时0.733秒.
3.1.3.1.3 1000 个并发用户/1万请求量(总)

3.1.3.2 调整方案二

增大新生代比例,新生代内部比例:8:1:1

java -jar -Xms3000m -Xmx3000m -Xmn2000m -XX:SurvivorRatio=8 spring-boot-interview-api-gateway.jar
3.1.3.2.1 10 个并发用户/1万请求量(总)
[zyxelva@172-104-123-96 15:01:53 ~]$ ab -c 10 -n 10000 http://127.0.0.1:8088/neo/heap
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8088

Document Path:          /neo/heap
Document Length:        7 bytes

Concurrency Level:      10
Time taken for tests:   13.771 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1390000 bytes
HTML transferred:       70000 bytes
Requests per second:    726.15 [#/sec] (mean)
Time per request:       13.771 [ms] (mean)
Time per request:       1.377 [ms] (mean, across all concurrent requests)
Transfer rate:          98.57 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    4   4.4      4     277
Processing:     1    9  15.1      7     281
Waiting:        0    7  15.2      5     277
Total:          2   13  15.3     11     285

Percentage of the requests served within a certain time (ms)
  50%     11
  66%     12
  75%     13
  80%     13
  90%     15
  95%     19
  98%     42
  99%     73
 100%    285 (longest request)

GC情况:

[zyxelva@172-104-123-96 15:03:07 ~]$ jstat -gc 4057 5000 20 | awk '{print $13,$14,$15,$16,$17 }'
YGC YGCT FGC FGCT GCT
24 0.534 2 0.122 0.656
25 0.556 2 0.122 0.678
34 0.615 2 0.122 0.737
42 0.662 2 0.122 0.785
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797
44 0.675 2 0.122 0.797

测试结果显示:

  • 用户的吞吐量大于在726.15/秒左右
  • JVM服务器平均请求处理时间1.377ms左右
  • JVM服务器发生了44次YGC,耗时0.675秒,还有2次FGC,0.122秒左右,加在一起GC耗时0.8秒.
3.1.3.2.2 100 个并发用户/1万请求量(总)
[zyxelva@172-104-123-96 15:13:11 ~]$ ab -c 100 -n 10000 http://127.0.0.1:8088/neo/heapThis is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8088

Document Path:          /neo/heap
Document Length:        7 bytes

Concurrency Level:      100
Time taken for tests:   13.431 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1390000 bytes
HTML transferred:       70000 bytes
Requests per second:    744.56 [#/sec] (mean)
Time per request:       134.308 [ms] (mean)
Time per request:       1.343 [ms] (mean, across all concurrent requests)
Transfer rate:          101.07 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   35  18.4     36     119
Processing:     4   98 202.7     58    2008
Waiting:        1   81 203.3     44    1976
Total:          7  133 201.7     98    2059

Percentage of the requests served within a certain time (ms)
  50%     98
  66%    116
  75%    132
  80%    140
  90%    163
  95%    186
  98%    787
  99%   1667
 100%   2059 (longest request)

GC情况:

[zyxelva@172-104-123-96 15:14:38 ~]$ jstat -gc 4057 5000 20 | awk '{print $13,$14,$15,$16,$17 }'
YGC YGCT FGC FGCT GCT
130 10.348 2 0.122 10.470
131 10.348 2 0.122 10.470
136 11.977 3 0.976 12.953
147 12.923 3 0.976 13.900
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237
151 13.261 3 0.976 14.237

测试结果显示:

  • 用户的吞吐量大于在744.56/秒左右
  • JVM服务器平均请求处理时间1.343ms左右
  • JVM服务器发生了151次YGC,耗时13.261秒,还有3次FGC,0.976秒左右,加在一起GC耗时14.237秒.

3.1.4 总结

本次测试只能做个参考,让大家理解下平时开发人员是怎样用AB来进行压测的,其他工具还有jmeter;另外,由于本机为笔记本,性能着实有限,实际开发过程中,应该利用单独的测试机,尽可能地避免其他因素干扰。这样做出来的结果才值得参考。

这里给出之前上课时,老师给的结果,他的机子比较牛,测试的是10万请求。仅供参考!

VM参数配置性能指标10 个并发/10万总请求100 个并发/10万总请求1000 个并发/10万总请求
默认:
最大堆内存为 480MB,
初始化大小为 32MB。
Eden 区 103m
From、To 3~4M
OLd =18
吞吐量
1426/s
1262/s
1145/s
平均处理时间
0.7ms
0.8ms
0.8ms
GC 耗时
17s
33s
42s
最大堆内存为 1.5G,
初始化大小为 1.5G
Eden 区 375m
From、To 62M
OLd =1000M
吞吐量
1205/s
989/s
749/s
平均处理时间
0.83ms
1.01ms
1.3ms
GC 耗时
34s
52s
75s
最大堆内存为 1.5G,
初始化大小为 1.5G
Eden 区 800m
吞吐量
1780/s
1927/s
1657/s
平均处理时间
0.56ms
0.51ms
0.6ms

一般情况下,高并发业务场景中,需要一个比较大的堆空间,而默认参数情况下,堆空间不会很大。所以我们有必要进行调整。

但是不要单纯的调整堆的总大小,要调整新生代和老年代的比例,以及 Eden 区还有 From 区、To 区的比例。 所以在我们上述的测试中,调整方案二(不是我的,是老师的方案),得到结果是最好的。在三种测试情况下都能够有非常好的性能指标,同时 GC 耗时相对控制也较好。

  • 对于调整方案一,就是单纯地加大堆空间,里面的比例不适合高并发场景,反而导致堆空间变大,没有明显减少 GC 的次数,但是每次 GC 需要检索对象的堆空间更大,所以 GC 耗时更长。
  • 而方案二:调整为一个很大的新生代和一个较小的老年代。原因是这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽存放长期存活对象

由于新生代空间较小,Eden 区很快被填满,就会导致频繁 Minor GC,因此我们可以通过增大新生代空间来降低 Minor GC 的频率。详见3.2.2.1节。

3.1.4 推荐策略

3.1.4.1 新生代大小选择

  • 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,新生代收集发生的频率也是最小的。同时,减少到达老年代的对象。
  • 吞吐量优先的应用:尽可能地设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
  • 避免设置过小:当新生代设置过小时会导致:
    • MinorGC 次数更加频繁
    • 可能导致Minor GC对象直接进入老年代,如果此时老年代满了,会触发Full GC。

3.1.4.2 老年代大小选择

  • 响应时间优先的应用:老年代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片,高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在新生代和老年代回收上的时间比例。
  • 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的新生代和一个较小的老年代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽存放长期存活对象。

3.2 GC优化

3.2.1 GC性能衡量指标

3.2.1.1 内存占用

垃圾回收过程,收集器也需要占用一定空间的内存,它是指为保证垃圾收集能够顺利高效地进行而存储的额外信息。比如CMSG1中的RSet(详见2.4节),特别是G1,根据经验,G1至少要耗费大约相当于Java堆容量10%至20%的额外内存来维持收集器工作。

随着计算机硬件的发展、性能的提升,我们越来越能容忍收集器多占用一点点内存;

硬件性能增长,对软件系统的处理能力是有直接助益的,硬件的规格和性能越高,也有助于降低收集器运行时对应用程序的影响,换句话说,吞吐量会更高。

3.2.1.2 吞吐量

这里的衡量吞吐量是指应用程序所花费的时间系统总运行时间比值。我们可以按照这个公式来计算 GC 的吞吐量:

系统总运行时间 = 应用程序耗时 + GC 耗时
如果系统运行了 100 分钟,GC 耗时 1 分钟,则系统吞吐量为 99%
GC 的吞吐量一般不能低于 95%

3.2.1.3 延迟

现阶段GC越来越看重延迟时间,假如一个垃圾收集器Stop the World太长,显然对于应用或者用户来说是比较难受的。就如同打游戏,内存就那么大,假如要清理一些垃圾而要暂停游戏一下,恐怕砸手机的心都有。

特别的,如同3.2.1.1节提到的,硬件性能增长,虽然吞吐量上去了,内存占用影响降低了,但对于延迟来说,反而是负面影响增大了。虚拟机要回收完整的1TB的堆内 存,毫无疑问要比回收1GB的堆内存耗费更多时间。

对于串行收集器而言,停顿时间可能会比较长;而使用并发收集器,由于垃圾收集器和应用程序交替运行,程序的停顿时间就会变短,但其效率很可能不如独占垃圾收集器,系统的吞吐量也很可能会降低。

下图中,浅色阶段表示必须挂起用户线程,深色则表示收集器线程与用户线程是并发工作的。

各收集器并发情况一览

由图可见:

  • CMSG1之前的全部收集器,其工作的所有步骤都会产生Stop The World式的停顿;
  • CMSG1分别使用增量更新原始快照(见2.2节)技术,实现了标记阶段的并发,不会因管理的堆内存变大,要标记的对象变多而导致停顿时间随之增长。但是对于标记阶段之后的处理,仍未得到妥善解决。
    • CMS使用标记-清除算法,虽然避免了整理阶段收集器带来的停顿,但是清除算法不论如何优化改进,在设计原理上避免不了空间碎片的产生,随着空间碎片不断淤积最终依然逃不过Stop The World的命运。
    • G1虽然可以按更小的粒度进行回收,从而抑制整理阶段出现时间过长地停顿,但毕竟也还是要暂停的。
  • 最后的两款收集器,ShenandoahZGC,几乎整个工作过程全部都是并发的,只有初始标记最终标记这些阶段有短暂地停顿,这部分停顿的时间基本上是固定的,与堆的容量堆中对象的数量没有正比例关系。实际上,它们都可以在任意可管理的堆容量下,实现垃圾收集的停顿都不超过十毫秒这种以前听起来是天方夜谭、匪夷所思的目标。

3.2.1.4 垃圾回收频率

通常垃圾回收的频率越低越好,增大堆内存空间可以有效降低垃圾回收发生的频率,但同时也意味着堆积的回收对象越多,最终也会增加回收时的停顿时间。所以我们需要适当地增大堆内存空间,保证正常的垃圾回收频率即可。

3.2.2 GC调优策略

3.2.2.1 降低 Minor GC 频率

由于新生代空间较小,Eden 区很快被填满,就会导致频繁 Minor GC,因此我们可以通过增大新生代空间来降低 Minor GC 的频率。

单次 Minor GC 时间是由两部分组成:T1(扫描新生代)和 T2(复制存活对象)。

  • 情况 1:假设一个对象在 Eden 区的存活时间为 500ms,Minor GC 的时间间隔是 300ms,因为这个对象存活时间>间隔时间,那么正常情况下,Minor GC 的时间为:T1+T2
  • 情况 2:当我们增大新生代空间,Minor GC 的时间间隔可能会扩大到 600ms,此时一个存活 500ms 的对象就会在 Eden 区中被回收掉,此时就不存在复制存活对象了,所以再发生 Minor GC 的时间为:T1*2(空间大了)+T2*0
    可见,扩容后,Minor GC 时增加了 T1,但省去了 T2 的时间。

在 JVM 中,复制对象的成本远高于扫描成本。如果在堆内存中存在较多的长期存活的对象,此时增加年轻代空间,反而会增加 Minor GC 的时间。如果堆中的短期对象很多,那么扩容新生代,单次 Minor GC 时间不会显著增加。因此,单次 Minor GC 时间更多取决于 GC 后存活对象的数量,而非 Eden 区的大小。

3.2.2.2 降低 Full GC 频率

由于堆内存空间不足老年代对象太多,会触发 Full GC,频繁的 Full GC 会带来上下文切换,增加系统的性能开销。 常用措施有:

  • 减少创建大对象:在平常的业务场景中,我们一次性从数据库中查询出一个大对象用于 web 端显示。比如,一次性查询出 60 个字段的业务操作,这种大对象如果超过年轻代最大对象阈值,会被直接创建在老年代;即使被创建在了年轻代,由于年轻代的内存空间有限,通过 Minor GC 之后也会进入到老年代。这种大对象很容易产生较多的 Full GC。
  • 增大堆内存空间:在堆内存不足的情况下,增大堆内存空间,且设置初始化堆内存为最大堆内存,也可以降低 Full GC 的频率。

3.2.2.3 选择合适的GC收集器

如果要求每次操作的响应时间必须在 500ms 以内。这个时候我们一般会选择响应速度较快的 GC 收集器,

  • 堆内存比较小的情况下(<6G)选择 CMS收集器
  • 堆内存比较大的情况下(>8G)G1收集器.

3.3 预估调优

3.3.1 调优分类

3.3.1.1 JVM 预调优

包括业务场景设定无监控不优化(要压测),具体步骤可拆分为:

  • 计算内存需求:并不是分配的内存越大越好,越大虽然可能对吞吐量或者内存吃紧带来正面影响,但响应时间(延迟)却不一定了。

    虚拟机栈的大小在高并发情况下可以变小。
    元空间(方法区)保险起见还是设定一个最大的值(默认情况下元空间是没有大小限制的),一般限定几百 M 就够用了.

  • 选定 CPU
  • 选择合适的垃圾收集器
  • 设定新生代大小、分代年龄
  • 设定日志参数

3.3.1.2 优化 JVM 运行环境(慢、卡顿等)

一般造成 JVM 卡或者慢的原因无非两个部分,一个是 CPU 占用过高,一个是内存占用过高。所以这个时候需要我们进行问题的排查,进行具体的故障分析。

3.3.1.3 解决 JVM 中的问题(OOM 等)

比如栈溢出堆溢出方法区溢出本机直接内存溢出等。

前面两节的内存优化GC 优化主要就是做的第一种(3.3.1.1节)和第二种(3.3.1.2节)工作。

4 实战

有空再搞。

5 总结

  • 具体问题具体分析,实事求是:GC 调优是个很复杂、很细致的过程,要根据实际情况调整,不同的机器、不同的应用、不同的性能要求调优的手段都是不同的,这些都需要大家平时去积累,去观察,去实践。
  • 明确调优思路:测试 - 分析 - 调优三步走。
  • 任何调优都需要结合场景,明确已知问题和性能目标,不能为了调优而调优,以免引入新的 Bug,带来风险和弊端。

文章作者: Kezade
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kezade !
评论
  目录