以前有一篇文章讲到JVM堆内存分代划分和对象在堆上的分配机制 ,详细可读 JVM堆内存解析,今天我们来聊一下怎么通过配置JVM参数,对系统性能进行优化,本文以JDK1.8做为实验环境
-Xmx:最大堆内存,如果程序运行超过这个值会报OOM,如果不设置一般默认为操作系统物理内存的1/4。
-Xms:初始堆内存,当初始堆不够,虚拟机会对堆空间扩展直至上限为最大堆空间,一般工作中将-Xms与-Xms设置为相等,这样可以减少垃圾回收次数。
-Xmn:新生代堆内存,一般默认为堆内存的1/4.
-XX:SurvivorRatio :设置Eden区与Survivor区的比值。
-XX:+PrintGC
打印GC简要信息
上面的日志发生了两次普通GC和一次FullGC,后面的数值表示 GC前堆内存使用大小,GC后内存使用大小,括号中是整个堆的大小。这里也可以看出FullGC耗时是普通GC的好几倍。
-XX:+PrintGCDetails
打印GC详细信息和堆的详细信息
第一次普通GC:[GC (Allocation Failure) [PSYoungGen: 404K->320K(1536K)] 3476K->3400K(5632K), 0.0006883 secs]
PSYoungGen: 404K->320K(1536K):新生代GC前后空间使用大小,3476K->3400K(5632K)整个堆内存GC前后空间使用大小。
第一次FullGC:[Full GC (Allocation Failure) [PSYoungGen: 288K->0K(1536K)] [ParOldGen: 3080K->1282K(4096K)]
3368K->1282K(5632K), [Metaspace: 2490K->2490K(1056768K)]
PSYoungGen:FullGC前后新生代空间使用大小,ParOldGen :FullGC前后老年代空间使用大小。
-Xloggc:log/gc.log:
将GC日志记录到外部文件
-XX:+TraceClassLoading
监控类的加载情况
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath
发生OOM异常时把堆栈信息打印到外部文件
下面一个程序连续向系统请求10M空间(每次请求1M),使用不同参数GC的情况完全不同。
1、java -Xmx20m -Xms20m -Xmn1m -XX:SurvivorRatio=2
-XX:+PrintGCDetails TestVM
由于eden区的空间大小为512K,无法容纳每次生成的1MB数组,这种就是大对象,直接分配在老年代,老年代最终占用内存为used 10240K。注:from区和to不受-XX:SurvivorRatio设置的影响,这是因为from和to已经是足够小了。
2、java -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=8
-XX:+PrintGCDetails TestVM 将新生代内存扩大到7M
由于eden区的空间大小为6M有足够的空间,但又并不足以有10M空间,所以发生了一次GC,由于每申请一次空间,同时也废弃上次申请的空间(上次申请的失去引用),做GC时被回收。老年代空间几乎没有被占用。
3、java -Xmx20m -Xms20m -Xmn15m -XX:SurvivorRatio=8
-XX:+PrintGCDetails TestVM 新生代分配15M空间
由于eden区的空间大小为12288K,完全满足10M数组的分配,因此没有触发GC,from/to区、老年代空间都为0.
总结:不同的堆空间分配对系统会产生一定的影响,基本策略是尽可能将对象预留在新生代,减少老年代GC的次数。
1、Java整个堆大小设置,Xmx 和 Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍。
2、老年代的内存大小设置为老年代存活对象的2-3倍。
3、年轻代Xmn的设置为老年代存活对象的1-1.5倍。
注:老年代FullGC后的内存占用,可以根据生产环境日志观察一段时间得出一个平均值。
maxMemory():最大内存,对应于参数-Xmx设置的值,如果没有设置,一般为物理内存的1/4。这表示可以从操作系统拿多少内存给JAVA进程使用,如果程序运行时占用的内存超过了这个数,就OOM了。
totalMemory():当前JVM内存量,程序运行时已经从系统中挖过来的内存。
freeMemory():从操作系统挖过来但又没有使用的内存。