JVM的参数类型
X参数
- 非标准参数
- -Xint: 解释执行
- -Xcomp: 第一次使用就编译成本地代码
- -Xmixed: JVM自己来决定是否编译成本地代码
默认使用的是mixed mode
用的不多, 只需要做了解, 用的比较多的是XX参数
XX参数
- 非标准化参数
- 相对不稳定
- 主要用来JVM调优和Debug
Boolean:
- 格式: -XX:[+-]<name> 表示启用或禁用name属性
- 比如: -XX:+UseConcMarkSweepGC , 表示启用了CMS垃圾收集器
- -XX:+UseG1GC , 表示启用了G1垃圾收集器
非Boolean类型:
- 格式: -XX:<name>=<value>表示name属性的值是value
- 比如:-XX:MaxGCPauseMillis=500 , GC的最大停顿时间为500ms
- -XX:GCTimeRatio=19, 表示垃圾回收时间最长不会超过总运行时间的 1/20
-Xmx -Xms
- 不是X参数, 而是XX参数
-Xms等价于-XX:InitialHeapSize
-Xmx等价于-XX:MaxHeapSize
查看JVM运行时参数
常见参数
- -XX:+PrintFlagsInitial , 查看程序运行的初始值
- -XX:+PrintFlagsFinal , 查看最终的值, 因为初始值可能会被修改(命令或程序修改)
- -XX:+UnlockExperimentalVMOptions , 解锁实验参数, 因为JVM中并不是所有的参数都可以直接赋值, 需要先使用这个参数才可以进行赋值
- -XX:+UnlockDiagnosticVMOptions , 解锁诊断参数
- -XX:+PrintcommandLineFlags , 打印命令行参数
使用示例: java -XX:+PrintFlagsFinal -version
这里查看的仅仅是调用java命令的当前进程的配置值, 后续会使用jinfo查看启动后的进程的参数配置
jps
可以根据网站查询具体的jps使用方法:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html#CHDCGECD
包含JVM命令的所有使用方法: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/index.html
jps
jps -l
jinfo
查看最大内存:
jinfo -flag MaxHeapSize <pid>
查看垃圾回收器:
jinfo -flag UseConcMarkSweepGC <pid> jinfo -flag UseG1GC <pid> jinfo -flag UseParallelGC <pid>
可以看出使用的是并行回收的垃圾回收器
jstat查看JVM统计信息
命令格式:
options :
- -class: 查看类加载信息
- -compiler: 查看编译信息
- -gc: 查看GC信息
- -printcompilation: 查看JIT编译信息
类装载
# 1000 输出间隔1000ms 10 表示输出总次数为10次 jstat -class <pid> 1000 10
垃圾收集
-gc , -gutil , -gccause , -gcnew , -gcold
所有的输出含义都可以在上文中提到的网站中进行查询
- Young区:
-
- S0C:当前 Survivor Space 0 的容量(kB)。
- S1C:当前 Survivor Space 1 的容量(kB)。
- S0U:Survivor Space 0 的利用率(kB)。
- S1U:Survivor Space 1 的利用率(kB)。
- EC:当前 Eden Space 的容量(kB)。
- EU :Eden Space 的利用率(kB)。
- Old区:
-
- OC:当前 Old Space 的容量(kB)。
- OU:Old Space 的利用率(kB)。
- MC:Metaspace 的容量(kB)。
- MU:Metaspace 的利用率(kB)。
- CCSC:压缩类空间的容量(kB)。
- CCSU:压缩类空间的使用情况(kB)。
- YGC:Young Generation 垃圾回收事件的数量。
- YGCT:Young Generation 垃圾回收时间。
- FGC:Full GC 事件的数量。
- FGCT:Full GC 时间。
- GCT:垃圾回收总时间。
JVM内存结构:
JIT编译
-compiler , -printcompilation
- Compiled:执行的编译任务数量。
- Failed:编译任务失败的数量。
- Invalid:被失效的编译任务数量。
- Time:执行编译任务所花费的时间。
- FailedType:最后一个失败编译任务的编译类型。
- FailedMethod:最后一个失败编译任务的类名和方法名。
jmap+MAT实战内存溢出
如何导出内存映像文件
- 内存溢出自动导出
-
- -XX:+HeapDumpOnOutOfMemoryError, 启用内存溢出时自动导出
- -XX:HeapDumpPath=./ , 指定导出文件的路径
- 使用jmap命令手动导出
-
- option: -heap, -clstats, -dump: <dump-options>, -F
- <none>: 打印共享对象映射
- -dump: 将Java堆内存以hprof二进制格式转储到文件中
- -finalizerinfo: 打印等待终结的对象信息
- -heap: 打印垃圾收集器使用情况、
- -histo: 打印堆直方图,包括Java类的对象数量、内存大小和完全限定类名
- -clstats: 打印每个类加载器的统计信息,包括名称、活动状态、地址、父类加载器以及已加载的类数和大小
- -F: 强制模式,用于在pid不响应时使用-dump或-histo选项
- -h或-help: 打印帮助信息
- -J flag: 将flag传递给运行jmap命令的Java虚拟机
# dump出运行程序的堆状态, 以二进制文件格式保存到当前命令路径下的heap.hprof jmap -dump:format=b,file=heap.hprof 48954
双击文件直接在IDEA中就可以直接查看分析结果:
分析内存溢出
测试触发内存溢出
测试com.imooc.monitor_tuning.chapter2.MemoryController#heap
idea程序启动运行参数:
-Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./data
请求: http://localhost:8080/heap
使用mat分析内存对象
使用mat查看:提示可能内存溢出的是某个controller
查看GC Roots, 排除弱引用,软引用等,只看强引用
分析得知: Tomcat的thread引用了一个MemoryController, MemoryController引用了一个UserList, list里面是所有的User对象
使用mat查看对象占的字节数
使用IDEA自带的进行分析
jstack实战死循环与死锁
一般CPU飙升很有可能就是死循环之类, 这时候就需要定位哪个线程出了问题
基本使用
- jstack [ options ] pid:打印指定进程 ID 的 Java 进程的堆栈跟踪信息。
- jstack [ options ] executable core:打印从指定 Java 可执行文件生成的核心转储文件的堆栈跟踪信息。
- jstack [ options ] [ server-id @ ] remote-hostname-or-IP:打印远程调试服务器主机名或 IP 地址上的 Java 进程的堆栈跟踪信息。
- -F:当 jstack [ -l ] pid 没有响应时,强制进行堆栈转储。
- -l:打印有关锁的其他信息,例如 java.util.concurrent 可拥有同步器的列表。有关 AbstractOwnableSynchronizer 类的说明,请参见 http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/AbstractOwnableSynchronizer.html 。
- -m:打印混合模式堆栈跟踪,其中包含 Java 和本地 C/C++ 帧。
其中,pid 表示进程 ID,executable 表示 Java 可执行文件,core 表示核心转储文件,remote-hostname-or-IP 表示远程调试服务器主机名或 IP 地址,server-id 是可选的唯一 ID,用于在同一远程主机上运行多个调试服务器时进行区分。jps 命令可以用于获取在计算机上运行的 Java 进程列表,jsadebugd 命令可以用于启动远程调试服务器。
简单实用:
# 指定pid简单输出分析一下 jstack 61091 > data/61091.txt
线程生命周期和状态:
具体可以参考: JAVA线程状态及状态间转换介绍 | 盖娅计划
死循环分析
打包程序为jar包到远程服务器进行运行
上传服务器:
# 需要改为你自己的服务器用户和ip地址 scp target/monitor_tuning-0.0.1-SNAPSHOT.jar root@172.16.237.144:/root/
登录服务器,然后启动服务:
java -jar monitor_tuning-0.0.1-SNAPSHOT.jar
浏览器访问: http://172.16.237.144:8080/loop , 可以多开几个窗口访问
再开一个服务端的端口进行查看负载和进程:
打印stack信息:
jstack 36850 > 36850.txt
查看对应的进程的线程信息:
# 用于查看进程 36850 的线程信息。具体来说,-p 选项指定要查看的进程 ID,-H 选项表示显示线程层次结构。 top -p 36850 -H
由于Stack的信息是16进制,所以需要打印一下:
printf "%x" 36870
得到对应的16进制的线程id为9006, 然后打开前面的打印的栈信息进行查看,
这里可以下载下来用一些软件查看,不建议使用vim直接在线上环境打开 36850.txt,这里只是测试这样查看
vim 36850.txt ## /9006 即可找到
定位到是com.imooc.monitor_tuning.chapter2.CpuController.getPartneridsFromJson 这个方法导致CPU升高, 最后检查代码进行修复
死锁分析
浏览器访问: http://172.16.237.144:8080/deadlock
jps
查看程序的java进程:
同样适用jstack打印栈信息:
jstack 37143 > 37143.txt vim 37143.txt
翻到文件末尾, 可以发现死锁的两个线程信息和对应的方法名