JVM的调优主要集中在内存上,包括内存大小和垃圾回收。因此了解JVM的内存模型是非常有必要的。
JMM由以下五个部分组成:
- 方法区,所有线程的共享区域,保存已加载的类,常量,静态变量,又称为永久代。这个区域内存耗尽会报java.lang.OutOfMemoryError: PermGen full
- 堆,所有线程的共享区域,分配对象实例,分为新生代(eden较大、survivor from、survivor to)和年老代(新生代中呆了足够长时间后移到这里)。堆中内存耗尽会报java.lang.OutOfMemoryError: Java heap space。
- 本地方法栈,线程私有,调用native方法时用的栈。
- 虚拟机栈,线程私有,保存局部变量、方法调用栈,递归过深会报StackOverflowError。如果虚拟机支持栈扩展,直到申请不到内存报OutOfMemoryError。
- 程序计数器,线程私有,指示下一个字节码,执行native方法时为空,不会报OutOfMemoryError。
JVM启动时有一系列参数可以设置各个区域的大小:
- -Xmx 设置堆大小的最大值,例如-Xmx1024m表示最大为1G
- -Xms 设置堆大小的最小值,也就是JVM启动时的堆大小,例如-Xms1024m表示最小为1G
- -Xmn 设置新生代大小的最大值,例如-Xmn512m表示新生代最大为512MB
- -XX:NewSize 设置新生代大小的最小值,也就是JVM启动时的新生代大小,例如-XX:NewSize=512m表示新生代最小为512MB
- -XX:SurvivorRatio 设置新生代中eden和survivor的大小比值,默认-XX:SurvivorRatio=8表示eden:from:to=8:1:1,也就是说如果-Xmn1000m,那么eden为800M,两个survivor各占100M。
- -XX:NewRatio 设置年老代和新生代的比值,默认-XX:NewRatio=2表示年老代大小是新生代2倍。-Xmn的优先级高于这个设置
- -XX:PermSize 设置永久代大小的最小值,也就是JVM启动时永久代的大小,默认-XX:PermSize=16m
- -XX:MaxPermSize 设置永久代大小的最大值,默认-XX:PermSize=64m
- -Xss 设置虚拟机栈的大小,例如-Xss128k。在一些java虚拟机中,本地方法栈和虚拟机栈合并实现为同一个。
- -XX:MaxDirectMemorySize 设置本地直接内存的最大值,一般就是DirectByteBuffer使用的内存,不属于JMM,如果直接内存达到最大值会触发Full GC。默认大小与-Xmx一样
为了增加吞吐量,生产环境一般设置为:
- -Xmx2048m -Xms2048m 两者一样避免堆大小反复震荡。jdk默认为内存大小的1/4,具体大小看情况,一般不超过可用内存的一半,如果确定程序中没有使用本地直接内存,可以设为更大。
- -Xss128k jdk默认是1M,减小栈大小来支持更多的线程
- -Xmn1024m 可以设大一点,避免新生代内存不够,对象直接进入年老代
- 永久代一般不用设置,除非应用中使用的类太多或者动态生成类太多
注意 以上内容基于HotSpot jdk1.7以前版本,jdk1.7以后有了不少改变,例如永久代变为Metaspace,不再存储字符串常量。-XX:PermSize变为-XX:MetaspaceSize,-XX:MaxPermSize变为-XX:MaxMetaspaceSize