Jvm 调试分析

JVM 参数

JVM 参数分为三类:标准参数、非标准参数(-X 参数)、高级选项(-XX 参数)

  1. 标准参数 所有的JVM实现都必须实现这些参数的功能,而且向后兼容; 如: -version、 -help

  2. 非标准参数 在不同版本的 jvm 中,参数可能会有所不同,可以通过 java -X 查看非标准参数。默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容; 如: 如 -Xms、-Xmx

  3. 高级选项 此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用,主要用于 JVM 的调优和 debug 操作。 -XX 参数的使用有 2 种方式,一种是 boolean 类型,一种是非 boolean 类型:

boolean 类型格式:-XX:[±] 非 boolean 类型格式:-XX

显示类加载树 TraceClassLoading

jvm 启动时显示类加载树 -XX:+TraceClassLoading

显示JVM 加载的class

在程序运行的时候有多少类被加载! 你可以用 verbose:class 来监视

-verbose:class

输出到文件 java -jar -verbose:class xxx.jar >> LoadedClassList.txt

classpath

-classpath

告知jvm搜索目录名, jar文档名, zip文档名, 之间用分号;分隔; 使用-classpath后jvm将不再使用CLASSPATH中的类搜索路径, 如果-classpath和CLASSPATH都没有设置, 则jvm使用当前路径(.)作为类搜索路径; jvm搜索类的方式和顺序为: Bootstrap, Extension, User; Bootstrap中的路径是jvm自带的jar或zip文件, jvm首先搜索这些包文件, 用System.getProperty("sun.boot.class.path")可得到搜索路径; Extension是位于JRE_HOME/lib/ext目录下的jar文件, jvm在搜索完Bootstrap后就搜索该目录下的jar文件, 用System.getProperty("java.ext.dirs")可得到搜索路径;

User搜索顺序为当前路径., CLASSPATH, -classpath, jvm最后搜索这些目录, 用System.getProperty("java.class.path")可得到搜索路径;

Djava.ext.dirs

添加依赖库查找目录

java -jar yang.jar -Djava.ext.dirs=./lib

内存相关参数

-XX:PermSize=64m //方法区分配的初始内存 -XX:MaxPermSize=64m //方法区所能占用的最大内存

-XX:MetaspaceSize //Java8+ 元空间初始大小 (等于原方法区) -XX:MaxMetaspaceSize// 元空间最大大小

-Xms10240m //堆内存 初始大小 -Xmx10240m //堆内存 最大大小

-Xss128k //线程栈大小

文件编码 Dfile.encoding

java默认字符串转byte[] (String.getBytes() ) 其编码是平台相关的!! 坑 -Dfile.encoding=UTF-8

JVM 分析神器 (arthas)

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

https://arthas.aliyun.com/doc/ https://github.com/alibaba/arthas

curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
# 键入进程编号
 
# 命令
dashboard

命令列表 dashboard - 当前系统的实时数据面板 getstatic - 查看类的静态属性 heapdump - dump java heap, 类似 jmap 命令的 heap dump 功能 jvm - 查看当前 JVM 的信息 logger - 查看和修改 logger mbean - 查看 Mbean 的信息 memory - 查看 JVM 的内存信息 ognl - 执行 ognl 表达式 perfcounter - 查看当前 JVM 的 Perf Counter 信息 sysenv - 查看 JVM 的环境变量 sysprop - 查看和修改 JVM 的系统属性 thread - 查看当前 JVM 的线程堆栈信息 vmoption - 查看和修改 JVM 里诊断相关的 option vmtool - 从 jvm 里查询对象,执行 forceGc

JVM 远程调试

应用启动参数

jdk 5-8: -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n java -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n -jar halo-1.5.3-SNAPSHOT.jar

jdk9以上: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005

 
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=192.168.40.171:5005 -jar jetlinks-standalone.jar
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=192.168.2.254:5000 -Dfile.encoding=UTF-8 -jar jetlinks-standalone.jar
 
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5630 -jar halo-1.5.3-m.jar
 
 
java -agentlib:jdwp=transport=dt_socket,address=*:5000,server=y,suspend=n -jar jetlinks-standalone.jar
 

IDE 设置

  • Eclipse 远程调试 设置 Debug configurationsRemote Java Application

  • IDEA 远程调试 Run edit configurations > (左上)+ Remote JVM Debug

JDK 的自带工具

jsp

jps是java自带的查看java进程的命令, 通过这个命令可以查看当前系统所有运行中的java进程, java包名, jar包名及JVM参数等;

  • 查看进程pid及完整包名及main方法参数: jps -ml

  • 查看pid及JVM参数: jps -v

官方 相关工具文档 官方 JPS文档

jstack

官网文档地址

使用jstack可查看指定进程(pid)的堆栈信息, 用以分析线程情况:

  • 打印堆栈信息

jstack -l pid

jstack -l 695701

  • 输出结果到日志中

jstack -l pid > /tmp/jstack.log

  • jstack检测死锁 jstack pid

Found one Java-level deadlock: … 省略信息 Found 1 deadlock

找出最耗CPU的线程 (for linux)

  1. 先找出最耗费CPU的进程 ps -Lfp pid 或者ps -mp pid -o THREAD, 或者top -Hp pid

TIME列就是各个Java线程耗费的CPU时间, CPU时间最长的是进程ID

  1. jstack 例如: 21711 其十六进制值为54ee

执行jstack jstack 21711 | grep 54ee

jmap

查看堆内存的配置情况及使用情况:

jmap -heap {PID}

查看对象创建数量:

jmap -histo {PID}

查看vm中内存对象的使用和占用大小

jstat -gccapacity {PID} 
NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC
5440.0  78464.0   7296.0  704.0  704.0   5888.0    10944.0   157056.0    14404.0    14404.0      0.0 1062912.0  15744.0      0.0 1048576.0   1920.0  13239    87

Dump 内存

jmap -dump:format=b,file={PATH}/jvm_dump.bin {PID}
 
# `live`选项会触发一次 Full GC,只转储存活的对象 ,也就是说 GC 收不走的对象
jmap -dump:live,format=b,file=heapdump.hprof {PID}

这种方式可以用 jvisualvm GUI工具进行内存分析, 或者采用 Eclipse Memory Analysis Tools (MAT)

Eclipse MAT 的分析思路

  1. ​打开堆转储文件​​后,MAT 通常会提供一个 ​​Leak Suspects Report​​(泄漏嫌疑报告)。这是一个非常好的起点,它会智能地分析可能的内存问题。
  2. 查看 Dominator Tree​​(支配树) 按照 ​​Retained Heap​​(保留大小)排序,保留大小指的是回收这个对象后能释放的总内存量。找到那些保留大小异常大的对象。
  3. 分析可疑对象​ 在支配树中右键点击可疑对象 ​Path To GC Roots exclude weak/soft references​ 这个操作会显示从该对象到 GC Roots 的完整引用链,并且​​排除了弱引用和软引用​​(因为它们不会阻止 GC)。剩下的就是​​强引用链​​,这就是导致它无法被回收的“罪魁祸首”!​​
  4. 查看 Histogram​​(直方图)按类名统计实例数量和总大小。看看哪个类的实例数量多得离谱

jconsole

集成以上三个等的 GUI管理工具

keytool

使用JDK自带工具KeyTool 生成自签发证书

keytool -genkey -alias nginx -keypass yang123. -keyalg RSA -keysize 2048 -validity 365 -keystore D:\software\nginx-1.14.2\conf\nginx.jks -storepass yang123.

-keypass yang123.(别名密码) -validity 365 有效期(天) -storepass yang123.(存储密码)

JKS 密钥库使用专用格式; 建议使用 “keytool -importkeystore -srckeystore D:\software\nginx-1.14.2\conf\nginx.jks -destkeystore D:\software\nginx-1 .14.2\conf\nginx.keystore -deststoretype pkcs12” 迁移到行业标准格式 PKCS12;

转为行业标准格式 PKCS12, 然后再用 openssl 提取公钥/私钥

javac 指定lib编译

扩展编译加执行的例子: javac TestWord.java -cp E:/PROJECTS-OA/jacob/lib/* java -cp E:/PROJECTS-OA/jacob/lib/*:jacob.jar net.jk.TestWord