常用JVM配置参数
大纲:
n Trace(轨迹/痕迹)跟踪参数
n 堆的分配参数
n 栈的分配参数
Trace跟踪参数
-verbose:gc
java -verbose:gc 中参数-verbose:gc 表示输出虚拟机中GC的详细情况.
使用后输出如下:
[Full GC 168K->97K(1984K), 0.0253873 secs]
解读如下:
箭头前后的数据168K和97K分别表示垃圾收集GC前后所有存活对象使用的内存容量,说明有168K-97K=71K的对象容量被回收,括号内的数据1984K为堆内存的总容量,收集所需要的时间是0.0253873秒(这个时间在每次执行的时候会有所不同)
-XX:+printGC
可以打印GC的简要信息
– [GC 4790K->374K(15872K), 0.0001606 secs]
– 4790K:GC前堆被使用的容量
– 374K:GC后堆被使用的容量
– 15872K:堆的总容量
– 0.0001606 secs:本次GC垃圾收集花费的时间
– [GC 4790K->374K(15872K), 0.0001474 secs]
– [GC 4790K->374K(15872K), 0.0001563 secs]
– [GC 4790K->374K(15872K), 0.0001682 secs]
参数配置和理解
1:参数分类和说明
jvm参数分固定参数和非固定参数
1):固定参数
如:-Xmx,-Xms,-Xmn,-Xss.
2):非固定参数
如:
-XX:+<option> 启用选项
-XX:-<option> 不启用选项
-XX:<option>=<number> 给选项设置一个数字类型值,可跟单位,例如 128k, 2g
-XX:<option>=<string> 给选项设置一个字符串值,例如-XX:HeapDumpPath=./dump.log
-XX:+PrintGCDetails
n -XX:+PrintGCDetails
– 打印GC详细信息
– 只要设置-XX:+PrintGCDetails 就会自动带上-verbose:gc和-XX:+PrintGC
– 在程序结束后,会把整个程序的堆的基本状况打印出来。
-XX:+PrintGCTimeStamps
n -XX:+PrintGCTimeStamps
– 打印CG发生的时间戳
n 例GC发生的时间戳
– [GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
– DefNew:说明这是一个新生代的GC信息
– 4416K:在GC之前,新生代被使用的内存大小
– 0K:GC之后,新生代被使用的内存大小
– 4928K:新生代的总内存大小
– 0.0001897 secs:新生代GC垃圾收集花费的时间
– 4790K:GC之前堆中总共被使用的内存大小
– 374K:GC之后堆中总共被使用的内存大小
– 15872K:堆的内存总大小
– 0.0002232 secs:整个堆GC垃圾收集所花费的时间
– Times:
– user=0.00:用户耗时
– sys=0.00:系统耗时
– real=0.00:实际耗时
s
n -XX:+PrintGCDetails在程序结束后的输出
– Heap[z1]
– def new generation[z2] total[z3] 13824K[z4] , used [z5] 11223K [0x27e80000[z6] (低边界), 0x28d80000[z7] (当前边界), 0x28d80000[z8] (最高边界)[z9] )
– eden space[z10] 12288K[z11] , 91% used [0x27e80000, 0x28975f20, 0x28a80000)
– from space[z12] 1536K, 0% used [0x28a80000, 0x28a80000, 0x28c00000)
– to space[z13] 1536K, 0% used [0x28c00000, 0x28c00000, 0x28d80000)
– tenured generation[z14] total 5120K, used 0K [0x28d80000, 0x29280000, 0x34680000)
– the space 5120K, 0% used [0x28d80000, 0x28d80000, 0x28d80200, 0x29280000)
– compacting perm gen[z15] total 12288K, used 142K [0x34680000, 0x35280000, 0x38680000)
– the space 12288K, 1% used [0x34680000, 0x346a3a90, 0x346a3c00, 0x35280000)
– ro space[z16] 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000)
– rw space[z17] 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000)
-Xloggc:log/gc.log
n -Xloggc:log/gc.log(意为:将GC日志信息输出到系统当前位置的log目录下的gc.log文件)
– 指定GC log日志的输出的位置,以文件输出(一般情况下,GC的日志输出都是在控制台的)
– 帮助开发人员分析问题
-XX:+PrintHeapAtGC
n -XX:+PrintHeapAtGC
– 每次一次GC的前后,都打印堆信息
{Heap before GC invocations=0[z18] (full 0)://GC前,堆内存的使用情况 def new generation total 3072K, used 2752K [0x33c80000, 0x33fd0000, 0x33fd0000) eden space 2752K, 100% used [0x33c80000, 0x33f30000, 0x33f30000) from space 320K, 0% used [0x33f30000, 0x33f30000, 0x33f80000) to space 320K, 0% used [0x33f80000, 0x33f80000, 0x33fd0000) tenured generation total 6848K, used 0K [0x33fd0000, 0x34680000, 0x34680000) the space 6848K, 0% used [0x33fd0000, 0x33fd0000, 0x33fd0200, 0x34680000) compacting perm gen total 12288K, used 143K [0x34680000, 0x35280000, 0x38680000) the space 12288K, 1% used [0x34680000, 0x346a3c58, 0x346a3e00, 0x35280000) ro space 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000) rw space 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000) [GC[DefNew: 2752K->320K(3072K), 0.0014296 secs] 2752K->377K(9920K), 0.0014604 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [z19] Heap after GC invocations=1[z20] (full 0)://GC之后,对内存的使用情况 def new generation total 3072K, used 320K [0x33c80000, 0x33fd0000, 0x33fd0000) eden space 2752K, 0% used [0x33c80000, 0x33c80000, 0x33f30000) from space 320K, 100% used [0x33f80000, 0x33fd0000, 0x33fd0000) to space 320K, 0% used [0x33f30000, 0x33f30000, 0x33f80000) tenured generation total 6848K, used 57K [0x33fd0000, 0x34680000, 0x34680000) the space 6848K, 0% used [0x33fd0000, 0x33fde458, 0x33fde600, 0x34680000) compacting perm gen total 12288K, used 143K [0x34680000, 0x35280000, 0x38680000) the space 12288K, 1% used [0x34680000, 0x346a3c58, 0x346a3e00, 0x35280000) ro space 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000) rw space 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000) } |
-XX:+TraceClassLoading
n -XX:+TraceClassLoading
– 监控类的加载,查看哪些类被加载到了虚拟机
• [Loaded java.lang.Object from shared objects file]
• [Loaded java.io.Serializable from shared objects file]
• [Loaded java.lang.Comparable from shared objects file]
• [Loaded java.lang.CharSequence from shared objects file]
• [Loaded java.lang.String from shared objects file]
• [Loaded java.lang.reflect.GenericDeclaration from shared objects file]
• [Loaded java.lang.reflect.Type from shared objects file]
-XX:+PrintClassHistogram
n -XX:+PrintClassHistogram
– 按下Ctrl+Break[z21] 后,打印类的信息:
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
– 分别显示:序号、实例数量、总大小、类型
堆的分配参数
-Xmx –Xms
n -Xmx –Xms
– 指定最大堆和最小堆
– -Xmx:最大堆,堆最大可以分配扩展到多大的空间。
– -Xms:最小堆,堆最小占用多少空间,系统启动时给堆初始分配多少空间。
n -Xmx20m -Xms5m 运行代码:
– 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=19.375M
free mem=4.342750549316406M
total mem=4.875M
n -Xmx20m -Xms5m 运行代码
– byte[] b=new byte[1*1024*1024];
– System.out.println("分配了1M空间给数组");
– 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");
打印输出:分配了1M空间给数组
Xmx=19.375M
free mem=3.4791183471679688M
total mem=4.875M
Java会尽可能维持在最小堆
n -Xmx20m -Xms5m 运行代码
n b=new byte[4*1024*1024];
n System.out.println("分配了4M空间给数组");
n System.out.print("Xmx=");
n System.out.println(Runtime.getRuntime().maxMemory()/1024.0/1024+"M");
n System.out.print("free mem=");
n System.out.println(Runtime.getRuntime().freeMemory()/1024.0/1024+"M");
n System.out.print("total mem=");
n System.out.println(Runtime.getRuntime().totalMemory()/1024.0/1024+"M");
打印输出:分配了4M空间给数组
Xmx=19.375M
free mem=3.5899810791015625M
total mem=9.00390625M(分配的总内存变多了)
n -Xmx20m -Xms5m 运行代码
n System.gc();
n System.out.println("回收内存");
n System.out.print("Xmx=");
n System.out.println(Runtime.getRuntime().maxMemory()/1024.0/1024+"M");
n System.out.print("free mem=");
n System.out.println(Runtime.getRuntime().freeMemory()/1024.0/1024+"M");
n System.out.print("total mem=");
n System.out.println(Runtime.getRuntime().totalMemory()/1024.0/1024+"M");
打印输出:回收内存
Xmx=19.375M
free mem=6.354591369628906M(空闲内存增多了)
total mem=10.75390625M
-Xmx 和 –Xms 应该保持一个什么关系,可以让系统的性能尽可能的好呢?
-Xmn
n -Xmn
– 设置新生代大小
-XX:NewRatio
n -XX:NewRatio
– 新生代(eden+2*s)和老年代(不包含永久区)的比值
– 如果设置为4(-XX:NewRatio=4) ,表示 新生代:老年代=1:4,即年轻代占堆的1/5
-XX:SurvivorRatio
n -XX:SurvivorRatio
– 设置两个Survivor(幸存)区和eden(伊甸区)的比
– 如果设置为8(-XX:SurvivorRatio=8),表示 两个Survivor :eden=2:8,即一个Survivor占年轻代的1/10
示例:
-Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails
代码:
public static void main(String[] args) { byte[] b=null; for(int i=0;i<10;i++) b=new byte[1*1024*1024]; } |
运行参数:
-Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails
日志打印:
结果分析
- 没有触发GC
- 全部分配在老年代
-Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails
代码:
public static void main(String[] args) { byte[] b=null; for(int i=0;i<10;i++) b=new byte[1*1024*1024]; } |
运行参数:
-Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails
日志打印:
实际占用空间大约10M 因为有系统级别的对象 如ClassLoader Thread等
结果分析:
- 没有触发GC
- 全部分配在eden
- 老年代没有使用
-Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails
代码:
public static void main(String[] args) { byte[] b=null; for(int i=0;i<10;i++) b=new byte[1*1024*1024]; } |
运行参数:
-Xmx20m -Xms20m -Xmn7m -XX:+PrintGCDetails
日志打印:
一共回收7M 剩余3M
S0 s1 太小无法周转
结果分析:
1.进行了2次新生代GC
2.s0 s1 太小需要老年代担保
-Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails
代码:
public static void main(String[] args) { byte[] b=null; for(int i=0;i<10;i++) b=new byte[1*1024*1024]; } |
运行参数:
-Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails
日志打印:
回收7M 剩余3M
结果分析:
1.进行了3次新生代GC
2.s0(from) s1(to) 增大
-Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=2 -XX:+PrintGCDetails
代码:
public static void main(String[] args) { byte[] b=null; for(int i=0;i<10;i++) b=new byte[1*1024*1024]; } |
运行参数:
-Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=2 -XX:+PrintGCDetails
日志打印:
结果分析:
比例分配,新生代 老年代对半开
对象全部留在新生代
-Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=3 -XX:+PrintGCDetails
代码:
public static void main(String[] args) { byte[] b=null; for(int i=0;i<10;i++) b=new byte[1*1024*1024]; } |
运行参数:
-Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=3 -XX:+PrintGCDetails
日志打印:
结果分析:
减少了s0(from) s1(to) GC数量变少,老年代未使用 空间使用率更高
-XX:+HeapDumpOnOutOfMemoryError
n -XX:+HeapDumpOnOutOfMemoryError
– 发生OOM异常时导出堆到文件
-XX:+HeapDumpPath
n -XX:+HeapDumpPath
– 发生OOM异常时,导出的堆放到文件的文件路径
示例:
-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump
运行参数:
-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump
代码及运行结果:
Vector v=new Vector(); for(int i=0;i<25;i++) v.add(new byte[1*1024*1024]); |
查看导出的堆文件:
-XX:OnOutOfMemoryError
n -XX:OnOutOfMemoryError
– 在发生OOM异常时,执行一个脚本
– 参数示例:"-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p[z22] ”
– printstack.bat 的内容:D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt
– 当程序OOM时,在D:/a.txt中将会生成线程的dump
– 也可以在OOM时,发送邮件,甚至是重启程序,主要看脚本怎么写。
堆的分配参数 – 总结
n 根据实际事情调整新生代和幸存代的大小
n 官方推荐新生代占堆的3/8
n 幸存代占新生代的1/10
n 在OOM时,记得Dump出堆,确保可以排查现场问题
永久区分配参数
-XX:PermSize -XX:MaxPermSize
n -XX:PermSize -XX:MaxPermSize
– 设置永久区的初始空间和最大空间
– 他们表示,一个系统可以容纳多少个类型(即可以加载多少个.class文件)
示例:
n 使用CGLIB等库的时候,可能会产生大量的类,这些类,有可能撑爆永久区导致OOM异常
代码:
for(int i=0;i<100000;i++){ CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());//不断产生新的类 } |
查看日志:
分析导致OOM异常的原因
n 打开堆的Dump(导出文件)
– 堆空间实际占用非常少
– 但是永久区溢出 一样抛出OOM
如果堆空间没有用完也抛出了OOM,有可能是永久区导致的
栈大小分配
-Xss
n -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(); } } } |
运行参数:
-Xss128K
输出结果:
deep of calling = 701
java.lang.StackOverflowError
运行参数:
-Xss256K
输出结果:
deep of calling = 1817
java.lang.StackOverflowError
经验总结:
栈空间越大,调用层次越深,去掉局部变量 调用层次可以更深
[z1]堆
[z2]新生代的内存状况
[z3]总共可用空间
[z4]12288K+ 1536K
[z5]已经使用的空间
[z6]该区间在内存中的起始位置
[z7]该区间当前已经分配的内存所到的位置。
[z8]该区间最高可以申请到的内存的位置。就目前的情况来看,新生代的当前边界等于最高边界,表示新生代的内存已经分配完了,不能再做扩展了。
[z9](0x28d80000-0x27e80000)
/1024/1024=15M
[z10]伊甸园区,对象出生的地方
[z11]总共可用的空间
[z12]GC复制算法,幸存代from区
[z13]GC复制算法,幸存代to区,他和from区的总内存大小一定是相等的。这两块区域是用于GC收集的复制算法的。这两块区域,只有一块是可用的,另一块是为复制算法准备的。互为备用,每次GC之后,相互切换角色。
[z14]老年代的内存的总体状况
[z15]永久区(方法区)的内存状况。Jdk5.0之后,会有一个共享区间,一些基础的java类会被加载到这个共享区间,供所有的jvm虚拟机使用。所以这里方法区被占用的空间不大。只有142k。
[z16]只读共享区间内存情况
[z17]可读可写共享区间内存情况。一些基础类主要是加载到了这两个共享区间,所以它们内存被使用率比较高。
[z18]调用0次GC的时候
[z19]GC的情况
[z20]调用1次GC的时候
[z21]老键盘在ESC行最右边,新键盘在方向键那块右上角,多媒体键盘和笔记本可能没有。用ctrl+c或
Esc替代。
[z22]%p代表的是当前这个java程序的pid,也就是进程id