前几篇文章和大家介绍了JVM中的内存模型以及垃圾回收器,今天和大家一起学习一下在平时jvm调优的过程中常用的参数以及命令。
我们经常用到的-XX类型有两种定义:
1. a.Boolean类型
格式: -XX:[+-]<name> +或-表示启用或者禁用name属性
比如: -XX:+UseConcMarkSweepGC 表示启用CMS类型的垃圾回收器
-XX:+UseG1GC 表示启用G1类型的垃圾回收器
2. b.非Boolean类型
格式: -XX<name>=<value>表示name属性的值是value
比如: -XX:MaxGCPauseMillis=500
还有一些常用的参数赋值方式,比如:
-Xms1000等价于-XX:InitialHeapSize=500 (设置JVM初始化时堆内存大小)
-Xmx1000等价于-XX:MaxHeapSize=1000 (设置JVM最大堆内存大小)
-Xss100等价于-XX:ThreadStackSize=100 (设置jvm中每个线程内存大小)
这里不详细展开讨论。
下图是网友整理好的JVM常用参数,分享给大家:
查看java进程
实时查看和调整JVM配置参数。例如: jinfo -flag name PID 查看某个java进程的name属性的值。
jinfo -flag MaxHeapSize PID
jinfo -flag UseG1GC PID
查看虚拟机性能统计信息,比如查看类装载信息:jstat -class PID 1000 10 查看某个java进程的类装载信息,每1000毫秒输出一次,共输出10次。
查看线程堆栈信息,比如: jstack PID。
这里值得说明的是,我们可以用这个命令来排查项目中的一些死锁情况,下面就模拟死锁场景:
代码如下:
//定义锁对象
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锁");
}
}
}
}
}
}
运行结果如下:
去看看该进程的堆栈信息:
这里的信息可以看出两个线程都在等对方解锁对象,导致线程死锁。
打印出堆内存相关信息: jmap -heap PID
dump出堆内存相关信息: jmap -dump:format=b,fifile=heap.hprof PID
如何设置当发生堆内存溢出的时候,能自动dump出该文件呢,这样便可以进行快速定位:
一般在开发中,JVM参数可以加上下面两句,这样内存溢出时,会自动dump出该文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof