深入JVM内核(二)常用的JVM配置参数
date: 2018-10-14 13:02:33
前言
目的:监控GC执行和JVM基本内存状态。
Trace跟踪参数
-verbose:gc
or -XX:+PrintGC
打印GC的简要信息
在虚拟机发生内存回收时在输出设备显示信息
实例:
/**
* @author ZenoYang
* with VM args: -verbose:gc -XX:+PrintGC
*/
public class TraceTest {
public static void main(String[] args) {
Object obj = new byte[1024*1024]; // 申请1M的空间
obj = null; // 把引用obj置为null好让GC回收刚刚申请的内存空间
System.gc(); // 调用GC
}
}
输出为:
[GC (System.gc()) 4355K->880K(125952K), 0.0018415 secs]
[Full GC (System.gc()) 880K->770K(125952K), 0.0083402 secs]
日志分析:
GC也叫Minor GC(年轻代GC),专门回收年轻代区域的内存。Full GC也叫Major GC(老年代GC),专门回收老年代区域的内存。4355K->880K表示GC回收了(4355-880)k的内存,两个125952K表示整个年轻代和年老代区域各占有125952K的内存,0.0018415 secs代表回收所花费的时间。
-XX:+PrintGCDetails
用于打印GC详细信息
还是刚才的例子(-XX:+PrintGCDetails
),输出为:
[GC (System.gc()) [PSYoungGen: 4355K->856K(38400K)] 4355K->864K(125952K), 0.0468308 secs] [Times: user=0.02 sys=0.00, real=0.05 secs]
[Full GC (System.gc()) [PSYoungGen: 856K->0K(38400K)] [ParOldGen: 8K->781K(87552K)] 864K->781K(125952K), [Metaspace: 3336K->3336K(1056768K)], 0.0223777 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
Heap
PSYoungGen total 38400K, used 333K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
eden space 33280K, 1% used [0x00000000d5e00000,0x00000000d5e534a8,0x00000000d7e80000)
from space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
to space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
ParOldGen total 87552K, used 781K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
object space 87552K, 0% used [0x0000000081a00000,0x0000000081ac3500,0x0000000086f80000)
Metaspace used 3342K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 361K, capacity 388K, committed 512K, reserved 1048576K
日志分析:
eden表示新生代,form和to表示两个幸存代(Survivor)区域,PSPermGen表示持久代区域。
[0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)分别代表低边界(起始的内存地址)、当前边界(当前占用到哪个位置)、最高边界(该区域最大能申请到的位置)
其它内容:emmm TODO
-XX:+PrintHeapAtGC
每次一次GC后,都打印堆信息
还是刚才的例子(-XX:+PrintHeapAtGC
),输出为:
{Heap before GC invocations=1 (full 0):
PSYoungGen total 38400K, used 4355K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
eden space 33280K, 13% used [0x00000000d5e00000,0x00000000d6240dc0,0x00000000d7e80000)
from space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
to space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
ParOldGen total 87552K, used 0K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
object space 87552K, 0% used [0x0000000081a00000,0x0000000081a00000,0x0000000086f80000)
Metaspace used 3153K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 342K, capacity 388K, committed 512K, reserved 1048576K
Heap after GC invocations=1 (full 0):
PSYoungGen total 38400K, used 840K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
eden space 33280K, 0% used [0x00000000d5e00000,0x00000000d5e00000,0x00000000d7e80000)
from space 5120K, 16% used [0x00000000d7e80000,0x00000000d7f52020,0x00000000d8380000)
to space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
ParOldGen total 87552K, used 8K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
object space 87552K, 0% used [0x0000000081a00000,0x0000000081a02000,0x0000000086f80000)
Metaspace used 3153K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 342K, capacity 388K, committed 512K, reserved 1048576K
}
{Heap before GC invocations=2 (full 1):
PSYoungGen total 38400K, used 840K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
eden space 33280K, 0% used [0x00000000d5e00000,0x00000000d5e00000,0x00000000d7e80000)
from space 5120K, 16% used [0x00000000d7e80000,0x00000000d7f52020,0x00000000d8380000)
to space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
ParOldGen total 87552K, used 8K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
object space 87552K, 0% used [0x0000000081a00000,0x0000000081a02000,0x0000000086f80000)
Metaspace used 3153K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 342K, capacity 388K, committed 512K, reserved 1048576K
Heap after GC invocations=2 (full 1):
PSYoungGen total 38400K, used 0K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
eden space 33280K, 0% used [0x00000000d5e00000,0x00000000d5e00000,0x00000000d7e80000)
from space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
to space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
ParOldGen total 87552K, used 744K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
object space 87552K, 0% used [0x0000000081a00000,0x0000000081aba178,0x0000000086f80000)
Metaspace used 3153K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 342K, capacity 388K, committed 512K, reserved 1048576K
}
日志分析:TODO
-XX:+TraceClassLoading
监控类的加载过程
还是刚才的例子(-XX:+TraceClassLoading
),输出为:
[Opened D:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar]
[Loaded java.lang.Object from D:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar]
[Loaded java.io.Serializable from D:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar]
[Loaded java.lang.Comparable from D:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar]
[Loaded java.lang.CharSequence from D:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar]
[Loaded java.lang.String from D:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from D:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar]
...(还有很多)
-XX:+PrintClassHistogram
打印类的信息
程序运行过程中,在控制台下按下Ctrl+Break时打印信息(不是上面的例子打印出来的信息):
num #instances #bytes class name
----------------------------------------------
1: 890617 470266000 [B
2: 890643 21375432 java.util.HashMap$Node
3: 890608 14249728 java.lang.Long
4: 13 8389712 [Ljava.util.HashMap$Node;
5: 2062 371680 [C
6: 463 41904 java.lang.Class
分别显示:序号、实例数量、总大小、类型
-Xloggc:log/gc.log
指定GC log的位置,以文件输出
重定向GC log,帮助开发人员分析问题
还是刚才的例子(-Xloggc:log/gc.log -XX:+PrintGCDetails
),log为文件夹,需要提前创建好,gc.log为产生的日志文件
堆的分配参数
-Xmx –Xms
指定最大堆和最小堆
实例,设置最大堆为20M,最小堆为5M:
/**
* with VM args: -Xmx20m -Xms5m
*/
public class HeapTest {
public static void main(String[] args) {
System.out.print("Xmx=");
System.out.println(Runtime.getRuntime().maxMemory()/1024.0/1024+"M");
System.out.print("free mem=");
System.out.println(Runtime.getRuntime().freeMemory()/1024.0/1024+"M");
System.out.print("total mem=");
System.out.println(Runtime.getRuntime().totalMemory()/1024.0/1024+"M");
}
}
输出为:
Xmx=18.0M
free mem=4.041107177734375M
total mem=5.5M
若分配1M空间给数组,在main方法最前面添加下面代码:
byte[] b=new byte[1*1024*1024];
System.out.println("分配了1M空间给数组");
输出为:
分配了1M空间给数组
Xmx=18.0M
free mem=3.017578125M
total mem=5.5M
这时候会发现剩余内存少了1M
若分配4M空间给数组:
byte[] b = new byte[4*1024*1024];
System.out.println("分配了4M空间给数组");
输出为:
分配了4M空间给数组
Xmx=18.0M
free mem=4.4584503173828125M
total mem=10.0M
这时候会发现,总内存变大了。这里做了堆的扩容。
若分配4M空间,再使用GC回收:
byte[] b = new byte[4*1024*1024];
System.out.println("分配了4M空间给数组");
System.gc();
输出为:
分配了4M空间给数组
Xmx=18.0M
free mem=6.177040100097656M
total mem=11.0M
会发现,剩余内存有增加了。
-Xmn
设置新生代大小(新生代包括一个eden区和两个Survivor幸存区)
实例:
VM args: -Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails
-XX:NewRatio
新生代(eden+2*s)和老年代(不包含永久区)的比值
4 表示 新生代:老年代=1:4,即年轻代占堆的1/5
-XX:SurvivorRatio
设置两个Survivor区和eden的比
8表示 两个Survivor :eden=2:8,即一个Survivor占年轻代的1/10
实例:
VM args: -Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=3 -XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
和-XX:+HeapDumpPath
-XX:+HeapDumpOnOutOfMemoryError: OOM时导出堆到文件
-XX:+HeapDumpPath: 导出OOM的路径
实例:
/**
* with VM args: -Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=e:/a.dump
*/
public class HeapTest {
public static void main(String[] args) {
Vector v = new Vector();
for(int i = 0; i < 25; i++) {
v.add(new byte[1024*1024]);
}
}
}
输出:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to e:/a.dump ...
Heap dump file created [16423219 bytes in 0.101 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at HeapTest.main(HeapTest.java:10)
此时在E盘下产生了dump文件
-XX:OnOutOfMemoryError
在OOM时,执行一个脚本
实例:
VM args: -XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p
printstack.bat:
D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt
当程序OOM时,在D:/a.txt中将会生成线程的dump
通过这个参数,可以在OOM时,发送邮件,或者重启程序等
堆分配的总结
- 根据实际情况调整新生代和幸存代的大小
- 官方推荐新生代占堆的3/8
- 幸存代占新生代的1/10
- 在OOM时,记得Dump出堆,确保可以排查现场问题
永久区参数分配
-XX:PermSize -XX:MaxPermSize
设置永久区的初始空间和最大空间
他们表示,一个系统可以容纳多少个类型
使用CGLIB等库的时候,可能会产生大量的类,这些类,有可能撑爆永久区导致OOM
for(int i = 0; i < 100000; i++) {
CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
}
所以,如果堆空间没有用完也抛出了OOM,有可能是永久区导致的
栈的大小分配
-Xss
通常只有几百K
决定了函数调用的深度
每个线程都有独立的栈空间
局部变量、参数 分配在栈上
实例:
public class TestStackDeep {
private static int count = 0;
public static void recursion(long a, long b, long c) {
long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10;
count++;
recursion(a, b, c);
}
public static void main(String args[]){
try{
recursion(0L,0L,0L);
}catch(Throwable e){
System.out.println("deep of calling = "+count);
e.printStackTrace();
}
}
}
当VM参数为-Xss128K
时:
java.lang.StackOverflowError
deep of calling = 425
...
当VM参数为-Xss256K
时:
deep of calling = 2051
java.lang.StackOverflowError
...
参考: