官方文档地址:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/index.html

常用 JVM 参数:

-Xms:初始堆大小,默认为物理内存的1/64(<1GB);默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制
-Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn:新生代的内存空间大小,注意:此处的大小是(eden+ 2 survivor space)。与jmap -heap中显示的New gen是不同的。整个堆大小=新生代大小 + 老生代大小 + 永久代大小。在保证堆大小不变的情况下,增大新生代后,将会减小老生代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8-XX:SurvivorRatio:新生代中Eden区域与Survivor区域的容量比值,默认值为8。两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10-Xss:每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。应根据应用的线程所需内存大小进行适当调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般小的应用, 如果栈不是很深, 应该是128k够用的,大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"-Xss is translated in a VM flag named ThreadStackSize”一般设置这个值就可以了。
-XX:PermSize:设置永久代(perm gen)初始值。默认值为物理内存的1/64-XX:MaxPermSize:设置持久代最大值。物理内存的1/4

JVM常用的命令

注意 命令加-help 可以查看命令的格式和相关参数

[root@localhost ~]# jps -help
usage: jps [-help]
       jps [-q] [-mlvV] [<hostid>]

Definitions:
    <hostid>:      <hostname>[:<port>]
[root@localhost ~]# jinfo -help
Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both of the above
    -h | -help           to print this help message

jps :基础工具

查看JAVA进程PID。
jps 命令用来查看所有 Java 进程,每一行就是一个 Java 进程信息。
jps 仅查找当前用户的 Java 进程,而不是当前系统中的所有进程,要显示其他用户的还只能用 ps 命令。
jps 常用参数

  • jps -l 如果是以 class 方式运行,会显示进程的主类 main.class 的全名,如果是以 jar 包方式运行的,就会输出 jar 包的完整路径名

第一列的数字就是进程的 pid

  • jps -v 输出传递给 JVM 的参数,v 表示虚拟机,jps -vl 比较常见的组合;
  • jps -V 大写 v,表示通过文件传递给 JVM 的参数
[root@localhost ~]# jps -vl
70737 sun.tools.jps.Jps -Denv.class.path=.:/opt/zhou/jdk1.8.0_144/jre/lib/rt.jar:/opt/zhou/jdk1.8.0_144/lib/dt.jar:/opt/zhou/jdk1.8.0_144/lib/tools.jar -Dapplication.home=/opt/zhou/jdk1.8.0_144 -Xms8m
69275 yiyun-0.0.1-SNAPSHOT.jar
  • jps -m 输出传递给 main.class 方法的参数,实用的一个命令,jps -ml 比较实用的组合,会显示包名/类名/参数
  • jps -q 只输出进程的 pid
[root@localhost ~]# jps -m
70816 Jps -m
69275 jar --spring.profiles.active=dev
[root@localhost ~]# jps -q
69275
70831

jps 是比较常用的 Java 命令。使用 jps 可以查看当前有哪些 Java 进程处于运行状态。如果运行了一个 web 应用(使用tomcat、jboss、jetty等启动)的时候,就可以使用 jps 查看启动情况。
有的时候我想知道这个应用的日志会输出到哪里,或者启动的时候使用了哪些javaagent,那么我可以使用 jps -v 查看进程的jvm参数情况。

jinfo:命令可以用来查看 Java 进程运行的 JVM 参数、

jinfo ${PID}查看某个JAVA进程中,JVM的参数值是什么。
jinfo -flags ${PID} 如果不加JVM参数的话,默认查看JVM中所有被修改过的值。

查看 JVM 参数:

# 查看pid
[root@localhost ~]# jps -l
71286 sun.tools.jps.Jps
69275 yiyun-0.0.1-SNAPSHOT.jar

[root@localhost ~]# jinfo -flags 69275
Attaching to process ID 69275, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.144-b01
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=478150656 -XX:MaxNewSize=159383552 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=10485760 -XX:OldSize=20971520 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps 
Command line:  
[root@localhost ~]# jinfo  69275
Attaching to process ID 69275, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.144-b01
Java System Properties:

java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.144-b01
sun.boot.library.path = /opt/zhou/jdk1.8.0_144/jre/lib/amd64
java.protocol.handler.pkgs = org.springframework.boot.loader
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = CN
user.dir = /opt/zhou/yiyun
java.vm.specification.name = Java Virtual Machine Specification
PID = 69275
java.runtime.version = 1.8.0_144-b01
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /opt/zhou/jdk1.8.0_144/jre/lib/endorsed
CONSOLE_LOG_CHARSET = UTF-8
line.separator = 

java.io.tmpdir = /tmp
java.vm.specification.vendor = Oracle Corporation
os.name = Linux
FILE_LOG_CHARSET = UTF-8
sun.jnu.encoding = UTF-8
java.library.path = /opt/rh/devtoolset-9/root/usr/lib64:/opt/rh/devtoolset-9/root/usr/lib:/opt/rh/devtoolset-9/root/usr/lib64/dyninst:/opt/rh/devtoolset-9/root/usr/lib/dyninst:/opt/rh/devtoolset-9/root/usr/lib64:/opt/rh/devtoolset-9/root/usr/lib:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
spring.beaninfo.ignore = true
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 3.10.0-1160.el7.x86_64
user.home = /root
user.timezone = Asia/Shanghai
catalina.useNaming = false
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
catalina.home = /tmp/tomcat.9400.3904281673976322911
user.name = root
java.class.path = yiyun-0.0.1-SNAPSHOT.jar
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = yiyun-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
java.home = /opt/zhou/jdk1.8.0_144/jre
user.language = zh
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.X11.XToolkit
java.vm.info = mixed mode
java.version = 1.8.0_144
java.ext.dirs = /opt/zhou/jdk1.8.0_144/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path = /opt/zhou/jdk1.8.0_144/jre/lib/resources.jar:/opt/zhou/jdk1.8.0_144/jre/lib/rt.jar:/opt/zhou/jdk1.8.0_144/jre/lib/sunrsasign.jar:/opt/zhou/jdk1.8.0_144/jre/lib/jsse.jar:/opt/zhou/jdk1.8.0_144/jre/lib/jce.jar:/opt/zhou/jdk1.8.0_144/jre/lib/charsets.jar:/opt/zhou/jdk1.8.0_144/jre/lib/jfr.jar:/opt/zhou/jdk1.8.0_144/jre/classes
java.awt.headless = true
java.vendor = Oracle Corporation
catalina.base = /tmp/tomcat.9400.3904281673976322911
com.zaxxer.hikari.pool_number = 1
file.separator = /
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.cpu.isalist = 

VM Flags:
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=478150656 -XX:MaxNewSize=159383552 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=10485760 -XX:OldSize=20971520 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps 
Command line:  

查看系统参数:

虚拟机的参数可以通过这个命令查看:

java -XX:+PrintFlagsFinal -version | grep manageable
[root@localhost ~]# java -XX:+PrintFlagsFinal -version | grep manageable
     intx CMSAbortablePrecleanWaitMillis            = 100                                 {manageable}
     intx CMSTriggerInterval                        = -1                                  {manageable}
     intx CMSWaitDuration                           = 2000                                {manageable}
     bool HeapDumpAfterFullGC                       = false                               {manageable}
     bool HeapDumpBeforeFullGC                      = false                               {manageable}
     bool HeapDumpOnOutOfMemoryError                = false                               {manageable}
    ccstr HeapDumpPath                              =                                     {manageable}
    uintx MaxHeapFreeRatio                          = 70                                  {manageable}
    uintx MinHeapFreeRatio                          = 40                                  {manageable}
     bool PrintClassHistogram                       = false                               {manageable}
     bool PrintClassHistogramAfterFullGC            = false                               {manageable}
     bool PrintClassHistogramBeforeFullGC           = false                               {manageable}
     bool PrintConcurrentLocks                      = false                               {manageable}
     bool PrintGC                                   = false                               {manageable}
     bool PrintGCDateStamps                         = false                               {manageable}
     bool PrintGCDetails                            = false                               {manageable}
     bool PrintGCID                                 = false                               {manageable}
     bool PrintGCTimeStamps                         = false                               {manageable}
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

除了通过启动脚本可以设置参数,PrintGC 默认是打开的,因此我们只需要打开 PrintGCDetails 参数。

jinfo -flag +PrintGC 69275
jinfo -flag +PrintGCDetails 69275

关闭 GC 日志的话同理:

jinfo -flag -PrintGC 69275
jinfo -flag -PrintGCDetails 69275

查看是否开启 GC 日志的打印:

jinfo -flag PrintGC 69275
jinfo -flag PrintGCDetails 69275
[root@localhost ~]# jinfo -flag PrintGC 69275-XX:-PrintGC
[root@localhost ~]# jinfo -flag PrintGCDetails 69275-XX:+PrintGCDetails

jstat:主要是对java应用程序的资源和性能进行实时的命令行监控,包括了对heap size和垃圾回收状况的监控

查看JVM中相关性能的信息。
jstat(Java Virtual Machine Statistics Monitoring Tool)是从JDK1.5自带的一个轻量级小工具。它位于java/bin目录下,主要利用JVM内建的指令对Java虚拟机的资源和性能进行实时的监控。

类装载信息:jstat -class ${PID}
举例:jstat -class ${PID} ${几秒内打印一次} ${打印10次} = jstat -class ${PID} 1000 10。打印出过去10秒类加载的数据。
GC相关的情况:jstat -gc ${PID}
举例:jstat -gc ${PID} ${几秒内打印一次} ${打印10次} = jstat -gc ${PID} 1000 10。打印出过去10秒GC的数据。

[root@localhost ~]# jstat -class 69275
Loaded  Bytes  Unloaded  Bytes     Time     
 8534 15758.4        0     0.0      22.90
[root@localhost ~]# jstat -gc 69275 
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT  
1472.0 1472.0  0.0    0.0   12032.0   3171.6   29872.0    17921.5   48728.0 45414.0 6272.0 5692.2     69    0.466   3      0.218    0.684

我们可以通过jstat -options查看可以支持的具体参数

[root@localhost ~]# jstat -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcmetacapacity
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcutil
-printcompilation

Option参数说明:

  • -class 类加载的行为统计
  • -compiler HotSpt JIT编译器行为统计
  • -gc 垃圾回收堆的行为统计
  • -gccapacity 各个垃圾回收代容量(young,old,meta)和他们相应的空间统计。
  • -gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因。
  • -gcmetacapacity 统计元空间使用情况
  • -gcnew 显示新生代统计信息
  • -gcnewcapacity 统计新生代及内存使用情况
  • -gcold 统计老年代和元空间使用情况
  • -gcoldcapacity 统计老年代内存使用情况
  • -gcutil 显示各个各代内存使用百分比
  • -printcompilation Hotspot方法编译统计情况

使用示例:

jstat -class : 类加载的行为统计

[root@localhost ~]# jstat -class 69275
 Loaded  Bytes  Unloaded  Bytes     Time    
  8534 15758.4        0     0.0      22.90
  • Loaded :加载class的数量
  • Bytes : 加载class的大小(单位KB)
  • Unloaded :卸载class的数量
  • Bytes: 卸载class的大小(单位KB)
  • Time : 加载和卸载class所耗费的时间

jstat -compiler :HotSpt JIT编译器行为统计

[root@localhost ~]# jstat -compiler 69275
Compiled Failed Invalid   Time   FailedType FailedMethod   
 4752      0       0    20.17          0    
  • Compiled :编译成功数
  • Failed : 编译失败数
  • Invalid : 无效数量
  • FailedType : 最后一次编译失效类型
  • FailedMethod :最后一次编译失效的方法

jstat -gc:垃圾回收堆的行为统计

[root@localhost ~]# jstat -gc 69275 
S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
1472.0 1472.0  0.0    0.0   12032.0   3171.6   29872.0    17921.5   48728.0 45414.0 6272.0 5692.2     69    0.466   3      0.218    0.684
  • S0C : 第一个幸存区的总容量(KB)
  • S1C : 第二个幸存区的总容量(KB)
  • S0U : 第一个幸存区已使用的容量(KB)
  • S1U : 第二个幸存区已使用的容量(KB)
  • EC : 伊甸区的总容量(KB)
  • EU : 伊甸区已使用容量(KB)
  • OC : 老年区的总容量(KB)
  • MC : 元空间的总容量(KB)
  • MU : 元空间已使用容量(KB)
  • CCSC : 压缩类空间总容量(KB)
  • CCSU : 压缩类空间已使用总容量(KB)
  • YGC : 新生代GC次数
  • YGCT :新生代GC总耗费时间
  • FGC : 老年代GC次数
  • FGCT : 老年代GC总耗费时间
  • GCT : GC总耗费时间

jstat -gccapacity : 各个回收区内存情况

[root@localhost ~]# jstat -gccapacity 69275
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC 
 10240.0 155648.0  14976.0 1472.0 1472.0  12032.0    20480.0   311296.0    29872.0    29872.0      0.0 1091584.0  48728.0      0.0 1048576.0   6272.0     69     3
  • NGCMN : 新生代占用的最小空间大小(KB)
  • NGCMX : 新生代占用的最大空间大小(KB)
  • NGC : 当前新生代空间大小(KB)
  • S0C : 第一幸存区当前空间大小(KB)
  • S1C :第二幸存区当前空间大小(KB)
  • EC : 伊甸区当前空间大小(KB)
  • OGCMN : 老年区最小空间大小(KB)
  • OGCMX : 老年区最大空间大小(KB)
  • OGC : 老年区当前空间大小(KB)
  • MCMN : 元空间最小空间大小(KB)
  • MCMX : 元空间最大空间大小(KB)
  • MC : 元空间当前空间大小(KB)
  • CCSMN : 压缩类空间最小大小(KB)
  • CCSMX : 压缩类最大空间大小(KB)
  • CCSC : 压缩类当前空间大小(KB)
  • YGC : 新生代GC次数
  • FGC : 老年代GC次数

jstat -gccause :垃圾收集统计概述

[root@localhost ~]# jstat -gccause 69275  
S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC                  
0.00   0.00  33.64  59.99  93.20  90.76     69    0.466     3    0.218    0.684 Allocation Failure   No GC  
  • S0 :第一幸存区已使用空间百分比
  • S1 : 第二幸存区已使用空间百分比
  • E : 伊甸区已使用空间百分比
  • O : 老年区已使用空间百分比
  • M : 元空间使用百分比
  • CCS : 压缩类空间使用百分比
  • YGC : 新生代GC次数
  • FGC : 老年代GC次数
  • LGCC :最近一次GC原因
  • GCC : 当前GC原因

gc -gcmetacapacity :元空间使用情况

[root@localhost ~]# jstat -gcmetacapacity 69275  
MCMN       MCMX        MC       CCSMN      CCSMX       CCSC     YGC   FGC    FGCT     GCT       
0.0  1091584.0    48728.0        0.0  1048576.0     6272.0    69     3    0.218    0.684
  • MCMN : 元空间最小空间大小(KB)
  • MCMX : 元空间最大空间大小(KB)
  • MC : 元空间当前空间大小(KB)
  • CCSMN : 压缩类空间最小空间大小(KB)
  • CCSMX : 压缩类空间最大空间大小(KB)
  • CCSC : 压缩类空间当前空间大小(KB)
  • YGC : 新生代GC次数
  • FGC : 老年代GC次数
  • FGCT: 老年代GC耗费时间
  • GCT : GC总耗费时间

jstat -gcnew : 显示新生代统计信息

[root@localhost ~]# jstat -gcnew 69275
S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT  
1472.0 1472.0    0.0    0.0  1  15  512.0  12032.0   4149.3     69    0.466
  • S0C : 第一幸存区总空间大小(KB)
  • S1C : 第二幸存区总空间大小(KB)
  • S0U : 第一幸存区已使用空间大小(KB)
  • S1U : 第二幸存区已使用空间大小(KB)
  • TT : 提升阈值(提升阈值)
  • MTT : 最大阈值
  • DSS : survivor区域大小 (KB)
  • EC : 伊甸区总空间大小(KB)
  • EU : 伊甸区已使用空间大小(KB)

** jstat -gcnewcapacity : 统计新生代及内存使用情况**

[root@localhost ~]# jstat -gcnewcapacity 69275  
NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX        EC      YGC   FGC    
10240.0   155648.0    14976.0  15552.0   1472.0  15552.0   1472.0   124544.0    12032.0    69     3
  • NGCMN : 新生代最小空间大小(KB)
  • NGCMX : 新生代最大空间大小(KB)
  • NGC : 当前新生代空间大小(KB)
  • S0CMX : 第一幸存区最大空间大小(KB)
  • S0C : 第一幸存区当前空间大小(KB)
  • S1CMX : 第二幸存区最大空间大小(KB)
  • S1C : 第二幸存区当前空间大小(KB)
  • ECMX : 伊甸区最大空间大小(KB)
  • EC : 伊甸区当前空间大小(KB)
  • YGC : 新生代GC次数
  • FGC : 老年代GC次数

** jstat -gcold : 统计老年代和元空间使用情况**

[root@localhost ~]# jstat -gcold 69275   
MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT     GCT    
48728.0  45414.0   6272.0   5692.2     29872.0     17921.5     69     3    0.218    0.684

jstat -gcoldcapacity :统计老年代内存使用情况

[root@localhost ~]# jstat -gcoldcapacity 69275   
OGCMN       OGCMX        OGC         OC       YGC   FGC    FGCT     GCT       
20480.0    311296.0     29872.0     29872.0    69     3    0.218    0.684
  • OGCMN :老年区占用最小空间(KB)
  • OGCMX : 老年区占用最大空间(KB)
  • OGC : 当前老年区空间(KB)
  • OC : 当前老年区空间(KB)
  • YGC :新生代GC次数
  • FGC : 老年代GC次数
  • FGCT : 老年代GC总耗时
  • GCT : GC总耗时

jstat -gcutil : 垃圾回收统计

[root@localhost ~]# jstat -gcutil 69275  
S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT     
0.00   0.00  35.51  59.99  93.20  90.76     69    0.466     3    0.218    0.684
  • S0 :第一幸存区已使用空间百分比.
  • S1 : 第二幸存区已使用空间百分比
  • E : 伊甸区已使用空间百分比
  • O : 老年区已使用空间百分比
  • M : 元空间使用百分比
  • CCS : 压缩类空间使用百分比
  • YGC : 新生代GC次数
  • FGC : 老年代GC次数
  • GCT :GC总耗时

jstat -printcompilation : Hotspot方法编译统计情况

[root@localhost ~]# jstat -printcompilation 69275
Compiled  Size  Type  Method    
 4760      4    1    com/zaxxer/hikari/util/ClockSource$NanosecondClockSource currentTime0
  • Compiled : 已编译方法次数
  • Size : 最近一次方法编译大小
  • Type : 最近一次编译方法类型
  • Method : 最近一次编译方法

jstack:查看某个Java进程内的线程堆栈信息

查看JAVA进程当中,线程内容。
jstack用法

[root@localhost ~]# jstack

Usage:
    jstack [-l] <pid>
        (to connect to running process) 连接活动线程
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process) 连接阻塞线程
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file) 连接dump的文件
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server) 连接远程服务器

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message

jstack查看输出

[root@localhost ~]# jstack -l 69275
2021-12-22 11:07:40
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode):

"Attach Listener" #39 daemon prio=9 os_prio=0 tid=0x00007fe7e8005800 nid=0x116af waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"http-nio-9400-exec-10" #28 daemon prio=5 os_prio=0 tid=0x00007fe7f8012000 nid=0x112b7 waiting on condition [0x00007fe7ec657000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000edaea830> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

"http-nio-9400-exec-9" #27 daemon prio=5 os_prio=0 tid=0x00007fe7f8010800 nid=0x112b6 waiting on condition [0x00007fe7ec758000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000edaea830> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

"http-nio-9400-exec-8" #26 daemon prio=5 os_prio=0 tid=0x00007fe7f800e800 nid=0x1127f waiting on condition [0x00007fe7ec859000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000edaea830> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:146)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1114)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- None

jstack统计线程数

[root@localhost ~]# jstack -l 69275 | grep 'java.lang.Thread.State' | wc -l
25

jstack检测cpu高
步骤一:查看cpu占用高进程

[root@localhost ~]# top
top - 11:10:15 up 1 day, 19:46,  2 users,  load average: 0.00, 0.01, 0.05
Tasks: 117 total,   1 running, 116 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1863004 total,   745788 free,   672536 used,   444680 buff/cache
KiB Swap:  2097148 total,  1926140 free,   171008 used.  1032660 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                         
 69275 root      20   0 2522504 166644   7784 S  0.7  8.9   0:28.17 java                                                                                                            
 11048 root      20   0  162580   2104    820 S  0.3  0.1   1:45.22 redis-server                                                                                                    
 11072 rabbitmq  20   0 1815140  52080   2092 S  0.3  2.8   2:33.75 beam.smp                                                                                                        
 70620 root      20   0  158904   5604   4260 S  0.3  0.3   0:00.39 sshd                                                                                                            
 73991 root      20   0  162108   2248   1556 R  0.3  0.1   0:00.05 top                                                                                                             
     1 root      20   0  128168   4988   3068 S  0.0  0.3   0:07.97 systemd                                                                                                         
     2 root      20   0       0      0      0 S  0.0  0.0   0:00.03 kthreadd                                                                                                        
     4 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kworker/0:0H                                                                                                    
     6 root      20   0       0      0      0 S  0.0  0.0   0:08.91 ksoftirqd/0                                                                                                     
     7 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0                                                                                                     
     8 root      20   0       0      0      0 S  0.0  0.0   0:00.00 rcu_bh                                                                                                          
     9 root      20   0       0      0      0 S  0.0  0.0   0:53.82 rcu_sched                                                                                                       
    10 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 lru-add-drain        

步骤二:查看cpu占用高线程

[root@localhost ~]# top -H -p 69275
top - 11:20:49 up 1 day, 19:56,  2 users,  load average: 0.11, 0.07, 0.06
Threads:  28 total,   0 running,  28 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1863004 total,   797084 free,   617716 used,   448204 buff/cache
KiB Swap:  2097148 total,  1942524 free,   154624 used.  1087456 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                         
 69275 root      20   0 2522504 168700   7784 S  0.0  9.1   0:00.01 java                                                                                                            
 69276 root      20   0 2522504 168700   7784 S  0.0  9.1   0:04.95 java                                                                                                            
 69277 root      20   0 2522504 168700   7784 S  0.0  9.1   0:00.97 java                                                                                                            
 69278 root      20   0 2522504 168700   7784 S  0.0  9.1   0:00.00 java                                                                                                            
 69279 root      20   0 2522504 168700   7784 S  0.0  9.1   0:00.01 java                                                                                                            
 69280 root      20   0 2522504 168700   7784 S  0.0  9.1   0:00.00 java                                                                                                            
 69281 root      20   0 2522504 168700   7784 S  0.0  9.1   0:10.76 java                                                                                                            
 69282 root      20   0 2522504 168700   7784 S  0.0  9.1   0:02.21 java                                                                                                                       

步骤三:转换线程ID

[root@localhost ~]# printf "%x\n" 69275        
10e9b

步骤四:定位cpu占用线程

jstack 69275|grep 10e9b -A 30

Jmap

jmap是JDK自带的工具软件,主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。可以使用jmap生成Heap Dump。在Java命令Java Dump和Java命令:Jstack中分别有关于Java Dump以及线程 Dump的介绍。

什么是堆Dump

基础知识

  • java虚拟机内存的组成以及堆内存介绍
  • java GC工作原理

常见内存错误:

  • outOfMemoryError 年老代内存不足。
  • outOfMemoryError:PermGen Space 永久代内存不足。
  • outOfMemoryError:GC overhead limit exceed 垃圾回收时间占用系统运行时间的98%或以上。

jmap 用法摘要

Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -permstat            to print permanent generation statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system
  • 指定进程号(pid)的进程 jmap [ option ]
  • 指定核心文件jmap [ option ]
  • 指定远程调试服务器 jmap [ option ] [server-id@]

参数:

  • option 选项参数是互斥的(不可同时使用)。想要使用选项参数,直接跟在命令名称后即可。
  • pid 需要打印配置信息的进程ID。该进程必须是一个Java进程。想要获取运行的Java进程列表,你可以使用jps。
  • executable 产生核心dump的Java可执行文件。
  • core 需要打印配置信息的核心文件。
  • remote-hostname-or-IP 远程调试服务器的(请查看jsadebugd)主机名或IP地址。
  • server-id 可选的唯一id,如果相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器。

选项:

  • 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。
  • -dump:[live,]format=b,file= 以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。
  • -finalizerinfo 打印等待终结的对象信息。
  • -heap 打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。
  • -histo[:live] 打印堆的柱状图。其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。
  • -permstat 打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
  • -F 强制模式。如果指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。
  • -h 打印帮助信息。
  • -help 打印帮助信息。
  • -J 指定传递给运行jmap的JVM的参数。

示例:
查看java 堆(heap)使用情况,执行命令: jmap -heap 69275

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

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration: //堆内存初始化配置
   MinHeapFreeRatio         = 40 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
   MaxHeapFreeRatio         = 70 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
   MaxHeapSize              = 478150656 (456.0MB)  //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
   NewSize                  = 10485760 (10.0MB) //对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
   MaxNewSize               = 159383552 (152.0MB) //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
   OldSize                  = 20971520 (20.0MB) //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
   NewRatio                 = 2 //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   SurvivorRatio            = 8 //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值 
   MetaspaceSize            = 21807104 (20.796875MB) //对应jvm启动参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小
   CompressedClassSpaceSize = 1073741824 (1024.0MB) //对应jvm启动参数-XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大大小
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage: //堆内存使用情况 
New Generation (Eden + 1 Survivor Space): //Eden区内存分布
   capacity = 13828096 (13.1875MB) //Eden区总容量
   used     = 11053496 (10.541435241699219MB) //Eden区已使用
   free     = 2774600 (2.6460647583007812MB)  //Eden区剩余容量
   79.9350539654917% used 
Eden Space:  //其中一个Survivor区的内存分布
   capacity = 12320768 (11.75MB)
   used     = 11053496 (10.541435241699219MB)
   free     = 1267272 (1.2085647583007812MB)
   89.71434248254654% used
From Space:  //另一个Survivor区的内存分布
   capacity = 1507328 (1.4375MB)
   used     = 0 (0.0MB)
   free     = 1507328 (1.4375MB)
   0.0% used
To Space: //当前的Old区内存分布
   capacity = 1507328 (1.4375MB)
   used     = 0 (0.0MB)
   free     = 1507328 (1.4375MB)
   0.0% used
tenured generation: //当前的 “永生代” 内存分布
   capacity = 30588928 (29.171875MB)
   used     = 18351576 (17.501426696777344MB)
   free     = 12237352 (11.670448303222656MB)
   59.99417828568559% used

18357 interned Strings occupying 1951456 bytes.

查看堆内存(histogram)中的对象数量及大小。执行命令: jmap -histo 69275

num     #instances         #bytes  class name
编号     个数                字节     类名
----------------------------------------------
   1:             7        1322080  [I
   2:          5603         722368  <methodKlass>
   3:          5603         641944  <constMethodKlass>
   4:         34022         544352  java.lang.Integer
   5:           371         437208  <constantPoolKlass>
   6:           336         270624  <constantPoolCacheKlass>
   7:           371         253816  <instanceKlassKlass>

将内存使用的详细情况输出到文件,执行命令: jmap -dump:format=b,file=heapDump 69275
这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用。

总结

1.如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。
2.要制作堆Dump可以直接使用jvm自带的jmap命令
3.可以先使用jmap -heap命令查看堆的使用情况,看一下各个堆空间的占用情况。
4.使用jmap -histo:[live]查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。
5.也可以使用 jmap -dump:format=b,file=命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容
6.在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。

来源:https://zhuanlan.zhihu.com/p/141783081

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐