JVM实战-JVM性能分析以及调优分析
1 JVM实战篇
1.1 JVM参数
1.1.1 标准参数
-version
-help
-server
-cp
1.1.2 -X参数
-Xint 解释执行
-Xcomp 第一次使用就编译成本地代码
-Xmixed 混合模式,JVM自己来决定
1.1.3 -XX参数
使用得最多的参数类型 非标准化参数,相对不稳定,主要用于JVM调优和Debug
a.Boolean类型
格式:-XX:[+-]<name> +或-表示启用或者禁用name属性
比如:-XX:+UseConcMarkSweepGC 表示启用CMS类型的垃圾回收器
-XX:+UseG1GC 表示启用G1类型的垃圾回收器
** b.非Boolean类型
格式:-XX<name>=<value>表示name属性的值是value
比如:-XX:MaxGCPauseMillis=500
1.1.4 其他参数
-Xms1000等价于-XX:InitialHeapSize=1000
-Xmx1000等价于-XX:MaxHeapSize=1000
-Xss100等价于-XX:ThreadStackSize=100
1.1.5 查看参数
java -XX:+PrintFlagsFinal -version > flags.txt
1.1.6 设置参数的方式
开发工具中设置比如IDEA,eclipse
运行jar包的时候:java -XX:+UseG1GC xxx.jar
web容器比如tomcat,可以在脚本中的进行设置
通过jinfo实时调整某个java进程的参数(参数只有被标记为manageable的flags可以被实时修改)
1.1.7 实践和单位换算
1Byte(字节)=8bit(位)
1KB=1024Byte(字节)
1MB=1024KB
1GB=1024MB
1TB=1024GB
(1)设置堆内存大小和参数打印
-Xmx100M -Xms100M -XX:+PrintFlagsFinal
(2)查询+PrintFlagsFinal的值
:=true
(3)查询堆内存大小MaxHeapSize
:= 104857600
(4)换算
104857600(Byte)/1024=102400(KB)
102400(KB)/1024=100(MB)
(5)结论
104857600是字节单位
1.1.8 常用参数含义
JVM参数说明:JVM参数
- 调优堆栈内存-堆栈大小典型配置参数
配置参数 | 说明 | 示例 |
---|---|---|
-Xmx | 设置最大堆大小。 | -Xmx3550m ,设置JVM最大可用内存为3550 MB。 |
-Xms | 设置JVM初始内存。 | -Xms3550m ,设置JVM初始内存为3550 MB。此值建议与-Xmx 相同,避免每次垃圾回收完成后JVM重新分配内存。 |
-Xmn | 设置年轻代大小。 | -Xmn2g ,设置年轻代大小为2 GB。整个JVM内存大小=年轻代大小+年老代大小+持久代大小。持久代一般固定大小为64 MB,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。 |
-Xss | 设置线程的栈大小。 | -Xss128k ,设置每个线程的栈大小为128 KB。说明 JDK 5.0版本以后每个线程栈大小为1 MB,JDK 5.0以前版本每个线程栈大小为256 KB。请依据应用的线程所需内存大小进行调整。在相同物理内存下,减小该值可以生成更多的线程。但是操作系统对一个进程内的线程个数有一定的限制,无法无限生成,一般在3000个~5000个。 |
-XX:NewRatio=n | 设置年轻代和年老代的比值。 | -XX:NewRatio=4 ,设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。如果设置为4,那么年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5。 |
-XX:SurvivorRatio=n | 年轻代中Eden区与两个Survivor区的比值。 | -XX:SurvivorRatio=4 ,设置年轻代中Eden区与Survivor区的大小比值。如果设置为4,那么两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6。 |
-XX:MaxPermSize=n | 设置持久代大小。 | -XX:MaxPermSize=16m ,设置持久代大小为16 MB。 |
-XX:MaxTenuringThreshold=n | 设置垃圾最大年龄。 | -XX:MaxTenuringThreshold=0 ,设置垃圾最大年龄。如果设置为0,那么年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,提高了效率。如果将此值设置为较大值,那么年轻代对象会在Survivor区进行多次复制,增加了对象在年轻代的存活时间,增加在年轻代即被回收的概率。 |
- 调优回收器GC-吞吐量优先的GC典型配置参数
配置参数 | 说明 | 示例 |
---|---|---|
-XX:+UseParallelGC | 选择垃圾收集器为并行收集器。 | -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 ,-XX:+UseParallelGC 此配置仅对年轻代有效,即在示例配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。 |
-XX:ParallelGCThreads | 配置并行收集器的线程数,即同时多少个线程一起进行垃圾回收。说明 此值建议配置与处理器数目相等。 | -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 ,-XX:ParallelGCThreads=20 表示配置并行收集器的线程数为20个。 |
-XX:+UseParallelOldGC | 配置年老代垃圾收集方式为并行收集。说明 JDK 6.0支持对年老代并行收集。 | -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC ,-XX:+UseParallelOldGC 表示对年老代进行并行收集。 |
-XX:MaxGCPauseMillis | 设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。 | -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 ,-XX:MaxGCPauseMillis=100 设置每次年轻代垃圾回收的最长时间为100 ms。 |
-XX:+UseAdaptiveSizePolicy | 设置此选项后,并行收集器自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时该间或者收集频率,该值建议使用并行收集器时,并且一直打开。 | -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAda |
- 响应时间优先的GC典型配置参数
配置参数 | 说明 | 示例 |
---|---|---|
-XX:+UseConcMarkSweepGC | 设置年老代为并发收集。说明 配置了-XX:+UseConcMarkSweepGC ,建议年轻代大小使用-Xmn 设置。 | -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC |
-XX:+UseParNewGC | 设置年轻代为并行收集。可与CMS收集同时使用。JDK 5.0以上版本,JVM根据系统配置自行设置,无需再设置此值。 | -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC |
-XX:CMSFullGCsBeforeCompaction | 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。 | -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection ,-XX:CMSFullGCsBeforeCompaction=5 ,表示运行GC5次后对内存空间进行压缩、整理。 |
-XX:+UseCMSCompactAtFullCollection | 打开对年老代的压缩。说明 该值可能会影响性能,但是可以消除碎片。 | -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection |
- 用于辅助的GC典型配置参数
配置参数 | 说明 |
---|---|
-XX:+PrintGC | 用于输出GC日志。 |
-XX:+PrintGCDetails | 用于输出GC日志。 |
-XX:+PrintGCTimeStamps | 用于输出GC时间戳(JVM启动到当前日期的总时长的时间戳形式)。示例如下:0.855: [GC (Allocation Failure) [PSYoungGen: 33280K->5118K(38400K)] 33280K->5663K(125952K), 0.0067629 secs] [Times: user=0.01 sys=0.01, real=0.00 secs] |
-XX:+PrintGCDateStamps | 用于输出GC时间戳(日期形式)。示例如下:2022-01-27T16:22:20.885+0800: 0.299: [GC pause (G1 Evacuation Pause) (young), 0.0036685 secs] |
-XX:+PrintHeapAtGC | 在进行GC前后打印出堆的信息。 |
-Xloggc:../logs/gc.log | 日志文件的输出路径。 |
全部参数查看:https://www.oracle.com/java/technologies/javase/vmoptions-jsp.html
1.2 常用命令
1.2.1 jps
查看java进程
1.2.2 jinfo
(1)实时查看和调整JVM配置参数
(2)查看
jinfo -flag name PID 查看某个java进程的name属性的值
(3)修改
参数只有被标记为manageable的flags可以被实时修改
jinfo -flag [+|-] PID
jinfo -flag = PID
(4)查看曾经赋过值的一些参数
jinfo -flags PID
1.2.3 jstat
(1)查看虚拟机性能统计信息
(2)查看类装载信息
jstat -class PID 1000 10 查看某个java进程的类装载信息,每1000毫秒输出一次,共输出10次
(3)查看垃圾收集信息
jstat -gc PID 1000 10
1.2.4 jstack
(1)查看线程堆栈信息
(2)用法
jstack PID
(3) 排查死锁
- DeadLockDemo
//运行主类
public class DeadLockDemo
{
public static void main(String[] args)
{
DeadLock d1=new DeadLock(true);
DeadLock d2=new DeadLock(false);
Thread t1=new Thread(d1);
Thread t2=new Thread(d2);
t1.start();
t2.start();
}
}
//定义锁对象
class MyLock{
public static Object obj1=new Object();
public static Object obj2=new Object();
}
//死锁代码
class DeadLock implements Runnable{
private boolean flag;
DeadLock(boolean flag){
this.flag=flag;
}
public void run() {
if(flag) {
while(true) {
synchronized(MyLock.obj1) {
System.out.println(Thread.currentThread().getName()+"----if获得obj1锁");
synchronized(MyLock.obj2) {
System.out.println(Thread.currentThread().getName()+"----if获得obj2锁");
}
}
}
}
else {
while(true){
synchronized(MyLock.obj2) {
System.out.println(Thread.currentThread().getName()+"----否则获得obj2锁");
synchronized(MyLock.obj1) {
System.out.println(Thread.currentThread().getName()+"----否则获得obj1锁");
}
}
}
}
}
}
- jstack 分析
1.2.5 jmap
(1)生成堆转储快照
(2)打印出堆内存相关信息
1.设置:-XX:+PrintFlagsFinal -Xms300M -Xmx300M
2.查看jmap -heap PID
(3)dump出堆内存相关信息、
jmap -dump:format=b,file=heap.hprof PID
jmap -dump:format=b,file=heap.hprof 44808
(4)要是在发生堆内存溢出的时候,能自动dump出该文件就好了
一般在开发中,JVM参数可以加上下面两句,这样内存溢出时,会自动dump出该文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof
设置堆内存大小: -Xms20M -Xmx20M
启动,然后访问localhost:9090/heap,使得堆内存溢出
(5)关于dump下来的文件
一般dump下来的文件可以结合工具来分析
1.3 常用工具
1.3.1 jconsole
JConsole工具是JDK自带的可视化监控工具。查看java应用程序的运行概况、监控堆信息、永久区使用 情况、类加载情况等。
启动命令:jconsole
1.3.2 jvisualvm
启动命令:jvisualvm
1.3.2.1 监控本地Java进程
1.3.2.2 监控远端Java进程
比如监控远端tomcat,演示部署在阿里云服务器上的tomcat
(1)在visualvm中选中“远程”,右击“添加”
(2)主机名上写服务器的ip地址,比如31.100.39.63,然后点击“确定”
(3)右击该主机“31.100.39.63”,添加“JMX”[也就是通过JMX技术具体监控远端服务器哪个Java进程]
(4)要想让服务器上的tomcat被连接,需要改一下 bin/catalina.sh 这个文件
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -
Djava.rmi.server.hostname=31.100.39.63 -Dcom.sun.management.jmxremote.port=8998
-Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=true -
Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access -
Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password"
(5)在 ../conf 文件中添加两个文件jmxremote.access和jmxremote.password
jmxremote.access 文件
guest readonly manager readwrite
jmxremote.password 文件
guest guest manager manager
授予权限 : chmod 600 * jmxremot *
(6)将连接服务器地址改为公网ip地址
hostname -i 查看输出情况
172.26.225.240 172.17.0.1
vim /etc/hosts
172.26.255.240 31.100.39.63
(7)设置上述端口对应的阿里云安全策略和防火墙策略
(8)启动tomcat,来到bin目录
./startup.sh
(9)查看tomcat启动日志以及端口监听
tail -f ../logs/catalina.out
lsof -i tcp:8080
(10)查看8998监听情况,可以发现多开了几个端口
lsof -i:8998 得到PID
netstat -antup | grep PID
(11)在刚才的JMX中输入8998端口,并且输入用户名和密码则登录成功
端口:8998
用户名:manager
密码:manager
1.3.3 Arthas
Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,是排查jvm相关问题的利器。
1.3.3.1 下载安装
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
# 然后可以选择一个Java进程
Print usage
java -jar arthas-boot.jar -h
1.3.3.2 常用命令
version:查看arthas版本号
help:查看命名帮助信息
cls:清空屏幕
session:查看当前会话信息
quit:退出arthas客户端
---
dashboard:当前进程的实时数据面板
thread:当前JVM的线程堆栈信息
jvm:查看当前JVM的信息
sysprop:查看JVM的系统属性
---
sc:查看JVM已经加载的类信息
dump:dump已经加载类的byte code到特定目录
jad:反编译指定已加载类的源码
---
monitor:方法执行监控
watch:方法执行数据观测
trace:方法内部调用路径,并输出方法路径上的每个节点上耗时
stack:输出当前方法被调用的调用路径
1.3.4 MAT
Java堆分析器,用于查找内存泄漏
Heap Dump,称为堆转储文件,是Java进程在某个时间内的快照
下载地址 :https://www.eclipse.org/mat/downloads.php
1.3.4.1 Dump信息包含的内容
- All Objects
Class, fields, primitive values and references
- All Classes
Classloader, name, super class, static fields
- Garbage Collection Roots
Objects defined to be reachable by the JVM
- Thread Stacks and Local Variables
The call-stacks of threads at the moment of the snapshot, and per-frame information about local objects
1.3.4.2 获取Dump文件
- 手动
jmap -dump:format=b,file=heap.hprof 44808
- 自动
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof
1.3.4.3 使用
- Histogram
Histogram可以列出内存中的对象,对象的个数及其大小
Class Name:类名称,java类名
Objects:类的对象的数量,这个对象被创建了多少个
Shallow Heap:一个对象内存的消耗大小,不包含对其他对象的引用
Retained Heap:是shallow Heap的总和,即该对象被GC之后所能回收到内存的总和
右击类名--->List Objects--->with incoming references--->列出该类的实例
右击Java对象名--->Merge Shortest Paths to GC Roots--->exclude all ...--->找到GC Root以及原因
- Leak Suspects
查找并分析内存泄漏的可能原因
Reports--->Leak Suspects--->Details
- Top Consumers
Top Consumers
1.3.5 GC日志分析工具
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
-Xloggc:gc.log
GCViewer