tomcat 的jvm 内存溢出问题的解决

转自:

http://blog.chinaunix.net/u1/56194/showart_1088998.html

最近在熟悉一个开发了有几年的项目,需要把数据库从mysql移植到oracle,首先把jdbc的连接指向mysql,打包放到tomcat里面,可以跑起来,没有问题,可是当把jdbc连接指向oracle的时候,tomcat就连续抛java.lang.OutOfMemoryError的错误,上网google了一下,了解了一下tomcat的运行机制,也解决了问题,share出来,以备查。 

1、首先是:java.lang.OutOfMemoryError: Java heap space 

解释: 

Heap size 设置 

JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。 
提示:在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。 
提示:Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。 

解决方法: 

手动设置Heap size 
修改TOMCAT_HOME/bin/catalina.bat,在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行: 

Java代码 
  1. set JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:MaxNewSize=256m   



或修改catalina.sh 
在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行: 
JAVA_OPTS="$JAVA_OPTS -server -Xms800m -Xmx800m -XX:MaxNewSize=256m" 

2、其次是:java.lang.OutOfMemoryError: PermGen space 

原因: 

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。 

解决方法: 

1. 手动设置MaxPermSize大小 
修改TOMCAT_HOME/bin/catalina.bat(Linux下为catalina.sh),在

Java代码 
  1. “echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:   
  2. set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=128M -XX:MaxPermSize=512m   



catalina.sh下为: 

Java代码 
  1. JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128M -XX:MaxPermSize=512m"  




另外看到了另外一个帖子,觉得挺好,摘抄如下: 
分析java.lang.OutOfMemoryError: PermGen space 

发现很多人把问题归因于: spring,hibernate,tomcat,因为他们动态产生类,导致JVM中的permanent heap溢出 。然后解决方法众说纷纭,有人说升级tomcat版本到最新甚至干脆不用tomcat。还有人怀疑spring的问题,在spring论坛上讨论很激烈,因为spring在AOP时使用CBLIB会动态产生很多类。 

但问题是为什么这些王牌的开源会出现同一个问题呢,那么是不是更基础的原因呢?tomcat在Q&A很隐晦的回答了这一点,我们知道这个问题,但这个问题是由一个更基础的问题产生。 

于是有人对更基础的JVM做了检查,发现了问题的关键。原来SUN 的JVM把内存分了不同的区,其中一个就是permenter区用来存放用得非常多的类和类描述。本来SUN设计的时候认为这个区域在JVM启动的时候就 固定了,但他没有想到现在动态会用得这么广泛。而且这个区域有特殊的垃圾收回机制,现在的问题是动态加载类到这个区域后,gc根本没办法回收! 


对于以上两个问题,我的处理是: 

在catalina.bat的第一行增加: 

Java代码 
  1. set JAVA_OPTS=-Xms64m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m   



在catalina.sh的第一行增加: 

Java代码 

JAVA_OPTS=-Xms64m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m   

JVM调优



1. Heap设定与垃圾回收

       Java Heap分为3个区,Young,Old和Permanent。Young保存刚实例化的对象。当该区被填满时,GC会将对象移到Old区。Permanent区则负责保存反射对象,本文不讨论该区。

       JVM的Heap分配可以使用-X参数设定,

       -Xms 初始Heap大小

       -Xmx java heap最大值

       -Xmn young generation的heap大小 



       JVM有2个GC线程。第一个线程负责回收Heap的Young区。第二个线程在Heap不足时,遍历Heap,将Young 区升级为Older区。Older区的大小等于-Xmx减去-Xmn,不能将-Xms的值设的过大,因为第二个线程被迫运行会降低JVM的性能。

       为什么一些程序频繁发生GC?有如下原因:

       1)程序内调用了System.gc()或Runtime.gc()。

       2)一些中间件软件调用自己的GC方法,此时需要设置参数禁止这些GC。

       3)Java的Heap太小,一般默认的Heap值都很小。

       4)频繁实例化对象,Release对象。此时尽量保存并重用对象,例如使用StringBuffer()和String()。

       如果你发现每次GC后,Heap的剩余空间会是总空间的50%,这表示你的Heap处于健康状态。许多Server端的Java程序每次GC后最好能有65%的剩余空间。

       经验之谈:

       1)Server端JVM最好将-Xms和-Xmx设为相同值。为了优化GC,最好让-Xmn值约等于-Xmx的1/3[2]。

       2)一个GUI程序最好是每10到20秒间运行一次GC,每次在半秒之内完成[2]。



       注意:

       1)增加Heap的大小虽然会降低GC的频率,但也增加了每次GC的时间。并且GC运行时,所有的用户线程将暂停,也 就是GC期间,Java应用程序不做任何工作。

       2)Heap大小并不决定进程的内存使用量。进程的内存使用量要大于-Xmx定义的值,因为Java为其他任务分配内存,例如每个线程的Stack等。



2.Stack的设定

       每个线程都有他自己的Stack。

       -Xss 每个线程的Stack大小 



       Stack的大小限制着线程的数量。如果Stack过大就好导致内存溢漏。-Xss参数决定Stack大小,例如-Xss1024K。如果Stack太小,也会导致Stack溢漏。

3.硬件环境

       硬件环境也影响GC的效率,例如机器的种类,内存,swap空间,和CPU的数量。

       如果你的程序需要频繁创建很多transient对象,会导致JVM频繁GC。这种情况你可以增加机器的内存,来减少Swap空间的使用[2]。

4.4种GC

       第一种为单线程GC,也是默认的GC。,该GC适用于单CPU机器。

       第二种为Throughput GC,是多线程的GC,适用于多CPU,使用大量线程的程序。第二种GC与第一种GC相似,不同在于GC在收集Young区是多线程的,但在Old区和第一种一样,仍然采用单线程。-XX:+UseParallelGC参数启动该GC。

       第三种为Concurrent Low Pause GC,类似于第一种,适用于多CPU,并要求缩短因GC造成程序停滞的时间。这种GC可以在Old区的回收同时,运行应用程序。-XX:+UseConcMarkSweepGC参数启动该GC。

       第四种为Incremental Low Pause GC,适用于要求缩短因GC造成程序停滞的时间。这种GC可以在Young区回收的同时,回收一部分Old区对象。-Xincgc参数启动该GC。

4种GC的具体描述参见[3]。



参考文章:

1. JVM Tuning. http://www.caucho.com/resin-3.0/performance/jvm-tuning.xtp#garbage-collection

2. Performance tuning Java: Tuning steps

http://h21007.www2.hp.com/dspp/tech/tech_TechDocumentDetailPage_IDX/1,1701,1604,00.html

3. Tuning Garbage Collection with the 1.4.2 JavaTM Virtual Machine .

http://java.sun.com/docs/hotspot/gc1.4.2/
JVM 调优,首先应从 内纯开始,尤其是在真正的的web服务部署的时候。因为真正的web服务会比开发的时候花费更多的内存,用来处理多用户并发的情况。本人多次吃过这方面的亏,

所以整理一下,希望能给别人以帮助。



       这个年头变啦,内存变得如大白菜,每个新装的机器都2G以上的内纯,甚至4G,也不是什么新闻。而软件‘吃’内存的情况则变化不大(除了VIsta)。但 JAVA诞生的时候可不是这样——

95年,想来当年97年,64M的内存还要500元,所以JVM初始化对内存的要不能太大,而且也要考虑老机器的情况,毕竟现在JRE基本跑在每个人的机器上。但是JVM初始占用还停留在

几年前的情况下,确实没有跟上软件和硬件的发展。而像Tomcat, JBoss, Eclipse(尤其安上MyEclipse插件后),也考虑到每台机器的内存情况,所以初始话定义都很低,

经常会抛内存溢出Bug。



    好,言归正传。我们先从解决bug开始,当Java程序申请内存,超出VM可分配内纯的时候,VM首先可能会GC,如果GC完还是不够,或者申请的直接超够VM可能有的,

就会抛出内存溢出异常。从VM规范中我们可以得到,一下几种异常。



     java.lang.StackOverflowError:(很少)

     java.lang.OutOfMemoryError:heap space(比较常见)

     java.lang.OutOfMemoryError: PermGen space (经常出现)



   以下分别解释一下,从最常见的开始:



       java.lang.OutOfMemoryError: PermGen space 这个异常比较常见,是说JVM里的Perm内存区的异常溢出,由于JVM在默认的情况下,Perm默认为64M,而很多程序

需要大量的Perm区内存,尤其使用到像Spring等框架的时候,由于需要使用到动态生成类,而这些类不能被GC自动释放,所以导致OutOfMemoryError: PermGen space异常。

解决方法很简单,增大JVM的 -XX:MaxPermSize 启动参数,就可以解决这个问题,如过使用的是默认变量通常是64M[5.0 and newer: 64 bit VMs are scaled 30% larger; 

1.4 amd64: 96m; 1.3.1 -client: 32m.],改成128M就可以了,-XX:MaxPermSize=128m。如果已经是128m(Eclipse已经是128m了),就改成 256m。我一般在服务器上为安全起见,改成256m。



     java.lang.OutOfMemoryError:heap space或其它OutOfMemoryError,这个异常实际上跟上面的异常是一个异常,但解决方法不同,所以分开来写。上面那个异常是因为

JVM的perm区内存区分少了引起的(JVM的内存区分为 young,old,perm三种)。而这个异常是因为JVM堆内存或者说总体分少了。解决方法是更改 -Xms -Xmx 启动参数,通常是扩大1倍。

xms是管理启动时最小内存量的,xmx是管里JVM最大的内存量的。

    注:OutOfMemoryError可能有很多种原因,根据JVM Specification, 可能有一下几种情况,我先简单列出。stack:stack分区不能动态扩展,或不足以生成新的线程。

Heap:需要更多的内存,而不能获得。 Method Area :如果不能满足分配需求。runtime constant pool(从Method Area分配内存)不足以创建class or interface。

native method stacks不能够动态扩展,或生成新的本地线程。



    最后说说java.lang.StackOverflowError,老实说这个异常我也没碰见过,但JVM Specification就提一下,规范上说有一下几种境况可能抛出这个异常,

一个是Stacks里的线程超过允许的时候,另一个是当native method要求更大的内存,而超过native method允许的内存的时候。根据SUN的文档,提高-XX:ThreadStackSize=512的值。



    总的来说调优JVM的内存,组要目的就是在使用内存尽可能小的,使程序运行正常,不抛出内纯溢出的bug。而且要调好最小内存,最大内存的比,避免GC时浪费太多时间,尤其是要尽量避免FULL GC。

我的使用例子,出现java.lang.OutOfMemoryError: PermGen space 时(服务器内存不足,或是eclipse中经常重启jboss),可以用下列参数或是适当修改以适应本机物理内存

-Xms512m -Xmx512m -XX:MaxNewSize=256m -XX:MaxPermSize=512m

原文地址:https://www.cnblogs.com/oisiv/p/2113609.html