黑马程序员技术交流社区

标题: 【上海校区】战胜Java OOM:JVM调优参数解释 [打印本页]

作者: wuqiong    时间: 2018-5-29 11:26
标题: 【上海校区】战胜Java OOM:JVM调优参数解释

最近很多做Java开发的朋友,包括Java Web开发,遇到OOM(out of memory)异常,都会一头雾水,或者只有一个粗浅的认识,内存溢出了,内存不足了。然后解决方法是,百度一堆设置Jvm内存大小的资料,然后copy到自己应用的配置文件里,重启应用,然而,却很少了解这些参数是什么意思,这些参数为什么有时候能解决问题,为什么有时候却不能解决问题。

这里,我打算写篇文章介绍一下我的认识,希望能帮到大家。

一、我们遇到的内存不足方面的异常见的是以下3种:

1、java.lang.OutOfMemoryError: Java heap space

2、java.lang.OutOfMemoryError: PermGen space

3、java.lang.StackOverflowError   


这里解释以下:

1、Java heap space:从字面上已经可看成,是堆内存不足。我们知道,jvm的内存结构大概可以分为:方法区(类信息、静态属性、常量池、方法代码等)、堆区(new出来的对象)、方法栈、本地方法栈、程序计数器。如果你对这几个区域不掌握,建议阅读周志明的《深入理解Java虚拟机》好好学习一下。

堆区,是Java垃圾回收的主要处理区域。如果你的程序要new很多类实例,而堆内存设置过小,即便有垃圾回收机制,也会出现空间不足的异常,在不考虑做代码优化的前提下,可以通过设置更大的jvm堆内存来解决这个问题。关于是哪些参数,下面再给出,看官们不用急哈。


2、PermGen space全称是Permanent Generation space,意思是永久代空间不足了。永久代其实就是上面说到的方法区,这部分一般不会进行GC(垃圾回收),其实在FULL GC里还是会进行回收的,不过一般回收的意义不会太大,所以,如果你的程序要load很多class的话,就可能会导致不足了。这里多说几句,在Java内存中,有新生代,老年代,永久代的说法,新生代,指新new出来的,刚刚使用到的,这部分资源每一次GC都会进行回收;老年代,顾名思义是已经存在好久了,一般是经过多次GC还存在内存的,这部分资源会因此而存留更久,需要FULL GC才能回收,新生代和老年代,都在堆区之中;永久代顾名思义是永生的,其实指的是方法区,当然,这里与新生代老年代不是一个平等意义上的概念了,因为新生代老年代通常都是对象,而永久代则不全是。 同理,下面再给出应该如何设置参数。


3、java.lang.StackOverflowError   这个明显是栈溢出了,栈的概念不陌生吧??栈溢出一般甚少出现,出现往往意味着程序逻辑有问题,例如无限递归,递归过多,调用过深等等。。


二、这里统一说一下jvm配置内存的一些参数,这里我将引用一位朋友的文章,说的很详细很精彩,我也就没有必要再自己写了,但是,提醒各位的是,很多参数或者大家不能理解,其实是因为大家对Java内存机制还没有深入的认识,这里再次推荐大家阅读周志明的《深入理解Java虚拟机》,或者国外的经典著作《深入Java虚拟机》,又或者阅读官方文档,只有掌握了这些知识,才能很好地理解并记住以下这些设置参数的意义


1、堆设置(这里是重点)
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代初始大小

-XX:MaxNewSize=n设置年轻代最大大小

-Xmn:相当于-XX:NewSize和-XX:MaxNewSize设置为同一个值了,表示永久设置年轻代的大小。当然剩下的就是老年代咯。

(下面两个先去看书吧~!)

-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5

2、方法区设置(重点)

-XX:PermSize=n:设置永久代初始大小
-XX:MaxPermSize=n:设置永久代最大大小


3、收集器设置(java支持多种垃圾收集器,当然你或许连收集器的概念也不知道,赶紧看书去吧)
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器


4、垃圾回收统计信息(这个可以用于调试时候查看GC的情况)
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename


5、并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)


6、并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。


7、栈区设置(一般甚少用到,放最后)

-Xss:每个线程的Stack大小,“-Xss 15120” 这使每增加一个线程(thread)就会立即消耗15M内存,而最佳值应该是128K,默认值好像是512k.


三、实例讲解(这里引用一位朋友的文字)



四、调优总结(此处想进阶一下,有问题莫喷)

1、年轻代大小选择
(1)响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。


(2)吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。


2、年老代大小选择
(1)响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
a.并发垃圾收集信息
b.持久代并发收集次数
c.传统GC信息
d.花在年轻代和年老代回收上的时间比例
减少年轻代和年老代花费的时间,一般会提高应用的效率


(2)吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。


3、较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
a.-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
b.-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩


五、对于JavaWeb开发的朋友,像我一样使用Tomcat,这里总结一下该在哪里设置tomcat的内存


1、解压版的Tomcat:  
修改(linux)TOMCAT_HOME/bin/catalina.sh (windows下是catalina.bat )
位置cygwin=false前。
JAVA_OPTS="-server -Xms256m -Xmx512m -XX:PermSize=64M -XX:MaxPermSize=128m"
2、安装版Tomcat:
(1)、 如果tomcat 5 注册成了windows服务,以services方式启动的,则需要修改注册表中的相应键值。
修改注册表HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Tomcat Service Manager\Tomcat5\Parameters\Java,右侧的Options
原值为
-Dcatalina.home="C:\ApacheGroup\Tomcat 5.0"
-Djava.endorsed.dirs="C:\ApacheGroup\Tomcat 5.0\common\endorsed"
-Xrs
加入 -Xms256m -Xmx512m
重起tomcat服务,设置生效

(2)、如果tomcat 6 注册成了windows服务,或者windows2003下用tomcat的安装版,
在/bin/tomcat6w.exe里修改就可以了 。

(其他tomcat版本请自行百度哦)
3、 如果要在eclipse中启动tomcat,上述的修改就不起作用了,可如下设置:
Myeclipse:
Myeclipse->preferences->myeclipse->servers->tomcat->tomcat×.×->JDK面板中的
Optional Java VM arguments中添加:-Xms256m -Xmx512m -XX:PermSize=64M -XX:MaxPermSize=128m
Eclipse:
右击某一个项目——》Run As——》Run Configurations——》VM argument中加入设置参数即可






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2