悄悄地告诉你:大白话带你认识JVM( 七 )

 

悄悄地告诉你:大白话带你认识JVM

文章插图
 
此时free memory就又缩水了,不过total memory是没有变化的 。Java会尽可能将total mem的值维持在最小堆内存大小
byte[] b = new byte[10 * 1024 * 1024];System.out.println("分配了10M空间给数组");System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");//系统的最大空间System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");//系统的空闲空间System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); 
悄悄地告诉你:大白话带你认识JVM

文章插图
 
这时候我们创建了一个10M的字节数据,这时候最小堆内存是顶不住的 。我们会发现现在的total memory已经变成了15M,这就是已经申请了一次内存的结果 。
此时我们再跑一下这个代码
System.gc();System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");//系统的最大空间System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");//系统的空闲空间System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); 
悄悄地告诉你:大白话带你认识JVM

文章插图
 
此时我们手动执行了一次fullgc,此时total memory的内存空间又变回5.5M了,此时又是把申请的内存释放掉的结果 。
#4.2 调整新生代和老年代的比值-XX:NewRatio --- 新生代(eden+2*Survivor)和老年代(不包含永久区)的比值
例如:-XX:NewRatio=4,表示新生代:老年代=1:4,即新生代占整个堆的1/5 。在Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置 。
#4.3 调整Survivor区和Eden区的比值-XX:SurvivorRatio(幸存代)--- 设置两个Survivor区和eden的比值
例如:8,表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10
#4.4 设置年轻代和老年代的大小-XX:NewSize --- 设置年轻代大小
-XX:MaxNewSize --- 设置年轻代最大值
可以通过设置不同参数来测试不同的情况,反正最优解当然就是官方的Eden和Survivor的占比为8:1:1,然后在刚刚介绍这些参数的时候都已经附带了一些说明,感兴趣的也可以看看 。反正最大堆内存和最小堆内存如果数值不同会导致多次的gc,需要注意 。
#4.5 小总结根据实际事情调整新生代和幸存代的大小,官方推荐新生代占java堆的3/8,幸存代占新生代的1/10
在OOM时,记得Dump出堆,确保可以排查现场问题,通过下面命令你可以输出一个.dump文件,这个文件可以使用VisualVM或者Java自带的Java VisualVM工具 。
-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=你要输出的日志路径一般我们也可以通过编写脚本的方式来让OOM出现时给我们报个信,可以通过发送邮件或者重启程序等来解决 。
#4.6 永久区的设置-XX:PermSize -XX:MaxPermSize初始空间(默认为物理内存的1/64)和最大空间(默认为物理内存的1/4) 。也就是说,jvm启动时,永久区一开始就占用了PermSize大小的空间,如果空间还不够,可以继续扩展,但是不能超过MaxPermSize,否则会OOM 。
tips:如果堆空间没有用完也抛出了OOM,有可能是永久区导致的 。堆空间实际占用非常少,但是永久区溢出 一样抛出OOM 。
#4.7 JVM的栈参数调优#4.7.1 调整每个线程栈空间的大小可以通过-Xss:调整每个线程栈空间的大小
JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K 。在相同物理内存下,减小这个值能生成更多的线程 。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右
#4.7.2 设置线程栈的大小-XXThreadStackSize:设置线程栈的大小(0 means use default stack size)这些参数都是可以通过自己编写程序去简单测试的,这里碍于篇幅问题就不再提供demo了
#4.8 (可以直接跳过了)JVM其他参数介绍形形色色的参数很多,就不会说把所有都扯个遍了,因为大家其实也不会说一定要去深究到底 。
#4.8.1 设置内存页的大小-XXThreadStackSize:设置内存页的大小,不可设置过大,会影响Perm的大小#4.8.2 设置原始类型的快速优化-XX:+UseFastAccessorMethods:设置原始类型的快速优化#4.8.3 设置关闭手动GC-XX:+DisableExplicitGC:设置关闭System.gc()(这个参数需要严格的测试)


推荐阅读