Tomcat:PermGen space永久存续区的内存溢出解决方法

 随着服务器上的tomcat部署的项目越来越多,最近在部署一个新的项目的时候出现内存溢出的错误

 

[plain] view plain copy

  1. Exception in thread "main" java.lang.OutOfMemoryError: PermGen space  
  2.         at java.lang.StackTraceElement.equals(StackTraceElement.java:204)  
  3.         at java.lang.Throwable.printEnclosedStackTrace(Throwable.java:688)  
  4.         at java.lang.Throwable.printStackTrace(Throwable.java:666)  
  5.         at java.lang.Throwable.printStackTrace(Throwable.java:720)  
  6.         at java.util.logging.SimpleFormatter.format(SimpleFormatter.java:157)  
  7.         at java.util.logging.StreamHandler.publish(StreamHandler.java:196)  
  8.         at java.util.logging.ConsoleHandler.publish(ConsoleHandler.java:105)  
  9.         at java.util.logging.Logger.log(Logger.java:522)  
  10.         at java.util.logging.Logger.doLog(Logger.java:543)  
  11.         at java.util.logging.Logger.logp(Logger.java:743)  
  12.         at org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:198)  
  13.         at org.apache.juli.logging.DirectJDKLog.error(DirectJDKLog.java:151)  
  14.         at org.apache.catalina.startup.Catalina.start(Catalina.java:648)  
  15.         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
  16.         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)  
  17.         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  
  18.         at java.lang.reflect.Method.invoke(Method.java:601)  
  19.         at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)  
  20.         at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:450)  
  21. Exception in thread "Timer-1" java.lang.OutOfMemoryError: PermGen space  
  22.         at sun.misc.Unsafe.defineClass(Native Method)  
  23.         at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:63)  
  24.         at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:399)  
  25.         at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:396)  
  26.         at java.security.AccessController.doPrivileged(Native Method)  
  27.         at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:395)  
  28.         at sun.reflect.MethodAccessorGenerator.generateConstructor(MethodAccessorGenerator.java:94)  
  29.         at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:48)  
  30.         at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
  31.         at java.lang.reflect.Constructor.newInstance(Constructor.java:525)  
  32.         at java.lang.Class.newInstance0(Class.java:372)  
  33.         at java.lang.Class.newInstance(Class.java:325)  
  34.         at oracle.net.ns.NSProtocol.connect(NSProtocol.java:250)  
  35.         at oracle.jdbc.driver.T4CConnection.connect(T4CConnection.java:1042)  
  36.         at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:301)  
  37.         at oracle.jdbc.driver.PhysicalConnection.(PhysicalConnection.java:531)  
  38.         at oracle.jdbc.driver.T4CConnection.(T4CConnection.java:221)  
  39.         at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)  
  40.         at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:503)  
  41.         at java.sql.DriverManager.getConnection(DriverManager.java:579)  
  42.         at java.sql.DriverManager.getConnection(DriverManager.java:221)  
  43.         at cn.com.pujiConvert.util.DbUtil.connect(DbUtil.java:37)  
  44.         at cn.com.pujiConvert.util.DbUtil.(DbUtil.java:25)  
  45.         at cn.com.pujiConvert.dao.Document.getDocumentInfo(Document.java:33)  
  46.         at cn.com.pujiConvert.convert.ConvertDocumentToPdf.getDocInfo(ConvertDocumentToPdf.java:49)  
  47.         at cn.com.pujiConvert.convert.ConvertDocumentToPdf.getConvertPdfResult(ConvertDocumentToPdf.java:204)  
  48.         at cn.com.pujiConvert.timer.ConvertTimerTask.convertDocumentToPdfTask(ConvertTimerTask.java:16)  
  49.         at cn.com.pujiConvert.timer.ConvertTimerTask.run(ConvertTimerTask.java:49)  
  50.         at java.util.TimerThread.mainLoop(Timer.java:555)  
  51.         at java.util.TimerThread.run(Timer.java:505)  

      以上提示说永久存续区的内存溢出。永久存续区的内存主要存储和加载Class和Meta信息,垃圾回收器不会回收这一区域的内存,只会回收堆内存。如何解决上面的永久存续期内存溢出问题呢?主要有两种方式

 

      增大jvm的非堆内存

        我们可以通过-xms和-xmax设定初始的和最大的永久存续区的内存大小。默认的永久存续期的初始内存为物理内存的1/64,最大的内存为1/4。若服务器操作系统为32位时,不能永久存续区的最大内存不能超过2G,64位操作系统不受此限制。

      通过tomcat设定shared lib目录方式

        虽然通过修改启动tomcat的jvm内存参数,增大vm的堆内存来解决可以解决这个问题,但是这个无法根本解决这个问题。

         我们通过tomcat设定shared lib实现所有项目共享相同的jar类包,通过这种方式有两个好处

1、避免各个项目重复加载相同的jar类包,占用了大量的永续区的内存

2、提高了tomcat的启动速度。因为减少了各个项目中的重复jar类包reload,tomcat的reload速度得到提升。

       配置shared lib

       打开进入tomcat/conf/目录。打开catalina.properties配置文件,修改两个地方

1、找到shared.loader=位置,设置共享目录地址

 

[html] view plain copy

  1. shared.loader=${catalina.base}/shared/lib,${catalina.base}/shared/lib/*.jar  

 

2、找到common.loader位置,并追加shared lib目录地址

[html] view plain copy

  1. common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,${catalina.base}/shared/lib,${catalina.base}/shared/lib/*.jar  

 

3、自行创建${catalina.base}/shared/lib,${catalina.base}/shared/lib/路径,并将共享的jar类包放在该目录下

     特别说明:jar共享目录可以在tomcat目录下,也可以自行定义一个绝对路径。如果是相对路径只能相对于${catalina.base}/。实现jar共享方式在以上shared.loader和common.loader中任选一种即可,不过个人倾向于使用shared.dir方式。

 

        原因分析

加载太多jar包和文件,分配Tomcat内存不够了,垃圾回收器GC 不会在主程序运行期对 PermGen space 进行清理。

解决方案
1、修改Catalina.bat文件,增加jvm内存,设定最大的永久存续区的内存大小

 

2、将项目公共的jar包提取到lib目录,所有项目共享相同的jar类包,避免项目间重复reload相同的jar包

 

 

       补充:toncat类加载机制

 

  • Commonclassloader:负责装载$CATALINA_HOME/common目录下的所有类和jar包,详细的配置可参考$CATALINA_HOME/conf/catalina.properties文件中的common.loader配置;该classloader装载的类对于Server class loader和Webapp class loader是可见的;Commonclass loader在Tomcat启动时创建,其parent classloader是System class loader;
  • Server classloader:负责装载Tomcat的核心类,位于$CATALINE_HOME/server目录下的所有类和jar,可由catalina.propreties中的server.loader配置指定;它在Tomcat启动时被创建,其parent loader是Commonclass loader;
  • Sharedclass loader:负责装载webapp公用的类,可以用户通过catalina.properties文件中的shared.loader属性来指定;它在Tomcat启动时被创建,其parentloader也是Common class loader;
  • Webappclassloader:这个比较特殊,它只负责加载各自app中WEB-INF/classes以及WEB-INF/lib下的类;其parentloader虽然是Shared class loader,但其加载策略和默认的类加载机制不太一样;

 

       No Spring WebApplicationInitializer types detected on classpath

         这个是因为spring 3.1以后在加载前会自动先扫描,查找WebApplicationInitializer的实现类

 

 

[html] view plain copy

  1. <listener>    
  2.     <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>    
  3. listener>   
  4. <context-param>  
  5.      <param-name>contextConfigLocationparam-name>  
  6.      <param-value>classpath:applicationContext.xmlparam-value>  
  7. context-param>  

         如果自行定义加载,不需要自动扫描,可以通过metadata-complete="true"关闭自动扫描

 

[html] view plain copy

  1. <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"  
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
  4. metadata-complete="true">   

 

       参考资料

         1、http://blog.sina.com.cn/s/blog_4db5ab6701013kuw.html

         2、http://blog.csdn.net/java_wliang/article/details/18044507

 

转载地址:https://blog.csdn.net/zouqingfang/article/details/48346321

你可能感兴趣的:(JDK)