【问题标题】:How to avoid Classloader Leak with JPA, Hibernate and Spring on Tomcat如何在 Tomcat 上使用 JPA、Hibernate 和 Spring 避免 Classloader 泄漏
【发布时间】:2013-08-17 09:29:28
【问题描述】:

Open J2EE Web Template 是一个展示应用程序 wicket - 在 Tomcat7 servlet 容器上运行的带有 Spring 和 Hibernate 的 JPA。它的 Maven 构建脚本似乎以标准方式使用组件。

但是,当它从 Tomcat 取消部署时,它会受到应用程序类加载器内存泄漏的影响。

Tomcat 的“查找泄漏”按钮确认泄漏。当使用 VM 选项 -XX:+HeapDumpOnOutOfMemoryError 部署在 Tomcat 上时,可以使用 Eclipse Memory Analyzer Tool (MAT) 分析生成的堆转储。 MAT 将 java.util.logging.Level$KnownLevel 类识别为阻止垃圾回收的罪魁祸首。

KnownLevel 构造函数的调试显示以下堆栈跟踪:

java.util.logging.Level$KnownLevel.add(Level.java:477) java.util.logging.Level.(Level.java:212) java.util.logging.Level.(Level.java:190) org.jboss.logging.JDKLevel.(JDKLevel.java:35) org.jboss.logging.JDKLevel.(JDKLevel.java:42) org.jboss.logging.JDKLogger.translate(JDKLogger.java:78) org.jboss.logging.JDKLogger.isEnabled(JDKLogger.java:85) org.jboss.logging.Logger.debugf(Logger.java:563) org.jboss.logging.LoggerProviders.find(LoggerProviders.java:37) org.jboss.logging.LoggerProviders.(LoggerProviders.java:32) org.jboss.logging.Logger.getLogger(Logger.java:2163) org.jboss.logging.Logger.getMessageLogger(Logger.java:2259) org.jboss.logging.Logger.getMessageLogger(Logger.java:2214) org.hibernate.ejb.Ejb3Configuration.(Ejb3Configuration.java:144) org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74) org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:268) org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225) org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291) org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:567) org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913) org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464) org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:385) org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:284) org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111) org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4939)

如果我正确理解Classloader leaks: the dreaded "java.lang.OutOfMemoryError: PermGen space" exception,那么这个类加载器泄漏是可以预料的。

避免这种情况的推荐方法是什么,或者 Spring/JPA 的替代方法是什么 Tomcat 上的模板 Web 应用程序?

【问题讨论】:

    标签: spring hibernate tomcat jpa jboss-logging


    【解决方案1】:

    我在 Tomcat7 + OpenJPA2.4.1 + ValidationAPI1.x + hibernate-validator-5.2.1 + jboss-logging.jar 上看到了同样的 webapp 内存泄漏

    问题确实是 jboss-logging.jar 创建了 java.util.logging.Level 实例的子类。如果 jar 由 J2EE 容器提供,则可能不是问题,但会在 mywebapp/WEB-INF/lib 分发中发生。 I have created a fork of package 禁用子类化。问题消失了,webapp 热重新部署工作正常。

    GC 根路径的 Heapdump 表明问题的根源,JDKLevel 子类将 webapp 保留在内存中,很快 JVM 就会耗尽 PermGen 内存。

    this     - value: org.apache.catalina.loader.WebappClassLoader #2
     <- <classLoader>     - class: org.jboss.logging.JDKLevel, value: org.apache.catalina.loader.WebappClassLoader #2
      <- <class>     - class: org.jboss.logging.JDKLevel, value: org.jboss.logging.JDKLevel class JDKLevel
       <- levelObject     - class: java.util.logging.Level$KnownLevel, value: org.jboss.logging.JDKLevel #6
        <- [1]     - class: java.lang.Object[], value: java.util.logging.Level$KnownLevel #12
         <- elementData     - class: java.util.ArrayList, value: java.lang.Object[] #5160 (10 items)
          <- value     - class: java.util.HashMap$Entry, value: java.util.ArrayList #3532
           <- [0]     - class: java.util.HashMap$Entry[], value: java.util.HashMap$Entry #21639
            <- table     - class: java.util.HashMap, value: java.util.HashMap$Entry[] #280 (16 items)
             <- intToLevels (sticky class)     - class: java.util.logging.Level$KnownLevel, value: java.util.HashMap #375
    

    edit为此错误创建了 JBoss Jira 票证 (https://issues.jboss.org/browse/JBLOGGING-118)

    【讨论】:

    • 非常感谢您的工作。您是否考虑向 jboss 日志项目提交可跟踪的问题,以便您的工作最终成为原始发行版的一部分?我建议这样做是因为我面临着额外的类加载器问题,我希望生成不会遇到此问题的基本测试用例。
    • @user250343 一旦我发现这个内存泄漏(github.com/jboss-logging/jboss-logging/pull/21)就已经完成了。我不知道合适的开发人员是否看到了我的请求。
    【解决方案2】:

    配置除 java.util.logging 之外的日志框架,例如Logback.

    如果根本没有配置日志记录,或者如果配置了 java.util.logging,jboss 日志记录将通过 JDK 错误Level.known can leak memory 创建类加载器泄漏。在 jboss logging 创建自定义 java.util.logging.Level 的问题中查看堆栈跟踪。

    【讨论】:

      猜你喜欢
      • 2014-02-25
      • 2012-02-09
      • 2013-09-23
      • 2010-12-22
      • 1970-01-01
      • 2021-09-09
      • 2016-02-21
      • 2020-04-23
      • 2020-05-13
      相关资源
      最近更新 更多