【问题标题】:Permgen Memory Leak with hot redeploy and Oracle database带有热重新部署和 Oracle 数据库的 Permgen 内存泄漏
【发布时间】:2013-04-30 01:09:00
【问题描述】:

我有一个在生产环境中运行的基于 SpringRoo 的应用程序,在一些热重新部署后会导致严重的 permgen 内存泄漏。

为了“发现并修复”泄漏并减少分析过程中的变量,我使用 roo 创建了一个简单、流线型的应用程序,并且获得了相同的行为。 该项目(使用 Spring Roo (1.2.3.RELEASE) 创建)仅保留一个名为“Person”的实体和一个名为“name”的字符串字段。

我将战争部署在 Tomcat 7.0.39 上,使用 Oracle 11.2.0.2 作为数据库。 每次重新部署后,我都会在 catalina.out 中收到此消息

INFO: Undeploying context [/ojdbc-0.1.0.BUILD-SNAPSHOT]
mag 06, 2013 10:50:43 AM org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
SEVERE: The web application [/ojdbc-0.1.0.BUILD-SNAPSHOT] registered the JDBC driver       [oracle.jdbc.OracleDriver] but failed to unregister it when the web application was stopped.    To prevent a memory leak, the JDBC Driver has been forcibly unregistered.

两次热重新部署后,我得到一个 permgen 错误

mag 06, 2013 10:51:08 AM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive /Applications/apache-tomcat-7.0.39/webapps/ojdbc-    0.1.0.BUILD-SNAPSHOT.war
Exception in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]"
Exception in thread "RMI TCP Connection(idle)" mag 06, 2013 10:51:17 AM ServerCommunicatorAdmin reqIncoming
WARNING: The server has decided to close this client connection.
java.lang.OutOfMemoryError: PermGen space
Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: PermGen space

我尝试使用 VisualVm / EclipseMemory Analyzer 对此进行分析,这就是我目前得到的结果。

事实是我没有观察到其他数据库(例如 PostgreSQL 或 Hypersonic)的这种行为。是否有与 Oracle 相关的东西导致泄漏?

Here 是一个 zip 存档,包含 roo 脚本生成器,以及 .hprof 转储文件。

【问题讨论】:

    标签: oracle tomcat memory-leaks spring-roo permgen


    【解决方案1】:

    试试这个:

    在 web xml 中注册一个 ServletContextListener

    <listener>
        <listener-class>my.package.MyShutdownServletContextListener</listener-class>
    </listener>
    

    来源:

    public class MyShutdownServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent event) {
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
            java.sql.DriverManager.getDrivers();
            Class.forName("oracle.jdbc.driver.OracleTimeoutThreadPerVM");
        } catch (ClassNotFoundException e) {
            /* noop */
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }
    @Override
    public void contextDestroyed(ServletContextEvent event) {}
    }
    

    【讨论】:

      【解决方案2】:

      有一个内部 Oracle 错误报告

      Oracle Support Bug 18707079 (JDBC THIN DRIVER LEAKS THREADS AND REFERENCES ON WEBAPP SHUTDOWN):https://support.oracle.com/epmos/faces/BugDisplay?id=18707079

      描述您的问题:

      When the webapp is shutdown, customer experience the following problems:
      1) The OracleDiagnosabilityMBean is not de-registered
      2) OracleTimeoutPollingThread does not stop
      3) The following thread does not stop: 
      oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser
      

      设置为Fixed in Product Version 12.2。希望它会尽快发布!

      【讨论】:

        【解决方案3】:

        尝试将 Oracle JDBC 驱动程序移到 Tomcat 的 lib 目录中,而不是放在应用程序的 lib 文件夹中。看起来 OracleDiagnosabilityMBean 正在处理 Catalina。

        编辑:由于您无法控制 Tomcat,因此请尝试包装 Oracle 类的加载位置(除了替换您的 Oracle 初始化的 AppContext):

        http://cdivilly.wordpress.com/2012/04/23/permgen-memory-leak/

        //somewhere in application startup, e.g. the ServletContextListener
        try {
         final ClassLoader active = Thread.currentThread().getContextClassLoader();
         try {
          //Find the root classloader
          ClassLoader root = active;
          while (root.getParent() != null) {
           root = root.getParent();
          }
          //Temporarily make the root class loader the active class loader
          Thread.currentThread().setContextClassLoader(root);
          //Force the AppContext singleton to be created and initialized
          sun.awt.AppContext.getAppContext();
         } finally {
         //restore the class loader
         Thread.currentThread().setContextClassLoader(active);   
        }
        } catch ( Throwable t) {
           //Carry on if we get an error
           LOG.warning("Failed to address PermGen leak");
        }
        

        【讨论】:

        • 好的,感谢您的回答,不幸的是,应用程序服务器不在我的管理之下...现在我已经将应用程序部署在一个临时的 tomcat 下,我可以随时重新启动。当然,这不是一个解决方案……但它是我迄今为止唯一的解决方案……
        • 编辑原始答案并添加额外内容
        • 好的,非常感谢,我试试看。我会让你知道结果。
        猜你喜欢
        • 2015-12-25
        • 2015-02-27
        • 1970-01-01
        • 1970-01-01
        • 2012-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-06
        相关资源
        最近更新 更多