tomcat内存溢出的三种情况说明
tomcat的内存溢出的本质其实就是JVM内存溢出,所以我们先了解以下java的JVM有关的内存知识。
JVM管理两种类型的内存,堆和非堆,堆是给开发人员用的,是在JVM启动时创建;非堆是留给JVM自己用的,用来存放Class和Meta信息的。它和堆不同,GC不会在主程序运行期对这块内存空间进行清理。
(1) 堆内存设置
JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置。JVM的初始空间(即-Xms)默认是物理内存的1/64;JVM最大空间(即-Xmx)默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。
因此一般情况下设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。可以利用JVM提供的-Xmn -Xms -Xmx等选项进行堆内存设置,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值,堆的最大值设置不要超过可用物理内存的80%。
(2) 非堆内存PermGen space(全称Permanent Generation space)
也称为永久保存的区域,用于存放Class和Meta信息,Class在被Load的时候被放入该区域。它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理。JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。GC不会对PermGen space进行清理,所以如果加载很多的Class的话,就很有可能出现PermGen space的错误。
以下我们针对tomcat内存溢出的三种情况做说明:
1. OutOfMemoryError:Java heap space 堆溢出
这种情况属于JVM堆溢出,在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候就会抛出此异常。
2. OutOfMemoryError:PermGen space 非堆溢出
这种错误常见于:
(1) web服务器对JSP进行编译的时候;
(2) 使用了大量的第三方jar, 其大小超过了jvm默认的大小(4M);
(3) 项目应用有太多的class文件而恰好MaxPermSize设置较小;
(4) tomcat热部署时侯不清理前面加载的环境,只将context更改为新部署的时候。
3. OutOfMemoryError:unable to create new native thread. 无法创建新的线程
这种现象比较少见,也比较奇怪,主要是和jvm与系统内存的比例有关。
具体的原因分析:每一个32位的进程最多可以使用2G的可用内存,因为另外2G被操作系统保留。这里假设使用1.5G给JVM,那么还余下500M可用内存。这500M内存中的一部分必须用于系统dll的加载,那么真正剩下的也许只有400M,现在关键的地方出现了:当你使用Java创建一个线程,在JVM的内存里也会创建一个Thread对象,但是同时也会在操作系统里创建一个真正的物理线程(参考JVM规范),操作系统会在余下的400兆内存里创建这个物理线程,而不是在JVM的1500M的内存堆里创建。在jdk1.4里头,默认的栈大小是256KB,但是在jdk1.5里头,默认的栈大小为1M每线程,因此,在余下400M的可用内存里边我们最多也只能创建400个可用线程。
这样结论就出来了,要想创建更多的线程,就需要调整分配给JVM的最大内存。还有一种做法是让JVM宿主在你的JNI代码里边。
下面总结一下java的JVM内存配置项:
-Xmx Java Heap最大值,默认值为物理内存的1/4;
-Xms Java Heap初始值,Server端JVM最好将-Xms和-Xmx设为相同值;
-Xmn Java Heap Young区的值;
-Xss 每个线程的Stack值;
-XX:PermSize:设定内存的永久保存区域;
-XX:MaxPermSize:设定最大内存的永久保存区域;
-XX:NewSize:设置JVM堆的‘新生代’的默认值;
-XX:MaxNewSize:设置JVM堆的‘新生代’的最大值;
一些参考配置方案:
1. 这种配置方案是我在eclipse集成tomcat做开发的时候使用的:
-Xms1024m -Xmx1024m -Xmn256m -XX:PermSize=256M -XX:MaxPermSize=512m
2. 这种配置方案是我在linux环境中配置tomcat的时候使用的:
在bin文件夹下面创建文件setenv.sh,并保存以下内容:
[root@localhost bin]# vim setenv.sh <!-- setenv.sh内容开始,其实就是声明环境变量 --> #!/bin/sh #Program: # set tomcat environment variables. #History: # 2016/09/29 wy JAVA_OPTS="-Xms1024m -Xmx1024m -Xmn256m" JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128M -XX:MaxPermSize=512m"
<!-- 保存以上内容到setenv.sh文件中 --> [root@localhost bin]# chmod -R 755 setenv.sh <!-- 更改setenv.sh权限,设置为可执行 -->