javax.xml.bind.DatatypeConverter泄漏类加载器?

时间:2012-07-06 11:06:48

标签: java tomcat jaxb classloader

我目前正在调查Tomcat 7(带有Oracle JDK 7)上的应用程序的一些类加载器泄漏。一个保持对Web应用程序类加载器的静态引用的类(因此导致类加载器不能在重新部署/重新启动时释放)是javax.xml.bind.DatatypeConverter,它位于系统类加载器中并通过其保留静态引用来自Sun的jaxb-impl软件包的theConverter字段到com.sun.xml.bind.DatatypeConverterImpl

以前有没有人见过这个问题?任何建议(除了在应用程序关闭时使用反射来清空静态字段)?

2 个答案:

答案 0 :(得分:13)

事实证明,我的一个依赖项(com.sun.jersey:jersey-json)已经引入了com.sun.xml.bind:jaxb-impl,它负责系统类加载器 - > Application Classloader参考。排除这种依赖性解决了这个问题(因为JDK 7附带了一个合理的JAXB实现,它将在System CL中引用,这很好)。

答案 1 :(得分:0)

在开发期间重新部署后续Web应用程序时出现Tomcat 8问题警告:

org.apache.catalina.loader.WebappClassLoaderBase checkThreadLocalMapForLeaks

SEVERE: The web application [rsnetlombard] created a ThreadLocal with key of type
[com.sun.xml.bind.v2.ClassFactory$1] (value [com.sun.xml.bind.v2.ClassFactory$1@79eb7926])
and a value of type [java.util.WeakHashMap]
(value [{class javax.xml.bind.annotation.W3CDomHandler=java.lang.ref.WeakReference@525eec52}])
but failed to remove it when the web application was stopped.
Threads are going to be renewed over time to try and avoid a probable memory leak.

我在VisualVM中进行堆转储并打开它。

VisualVM通过查询在OQL选项卡中查找已销毁的Web应用程序类加载器:

select x from org.apache.catalina.loader.WebappClassLoader x where x.state.name.toString() == "DESTROYED"

在"安装"中访问指向对象的指向链接选项卡允许调用"查找最近的GC根"在"参考 部分"并将文本表示复制到clipboard ::

this     - value: org.apache.catalina.loader.WebappClassLoader #3
 <- <classLoader>     - class: com.sun.xml.bind.DatatypeConverterImpl, value: org.apache.catalina.loader.WebappClassLoader #3
  <- <class>     - class: com.sun.xml.bind.DatatypeConverterImpl, value: com.sun.xml.bind.DatatypeConverterImpl class DatatypeConverterImpl
   <- theConverter (sticky class)     - class: javax.xml.bind.DatatypeConverter, value: com.sun.xml.bind.DatatypeConverterImpl #1

javax.xml.bind.DatatypeConverter来自Java SE,该类由系统类加载器加载 (标有(sticky class))。但是指向由Web应用程序类加载器加载的类。

关于com.sun.xml.bind.DatatypeConverterImpl的Google搜索会导致此SO帖子。

提供的解决方案说com.sun.jersey:jersey-json包请求JAXB API实现 来自com.sun.xml.bind:jaxb-impl package ::

$ mvn dependency:tree
...
[INFO] +- com.sun.jersey:jersey-json:jar:1.8:compile
[INFO] |  +- org.codehaus.jettison:jettison:jar:1.1:compile
[INFO] |  |  \- stax:stax-api:jar:1.0.1:compile
[INFO] |  +- com.sun.xml.bind:jaxb-impl:jar:2.2.3-1:compile
[INFO] |  |  \- javax.xml.bind:jaxb-api:jar:2.2.2:compile
[INFO] |  |     \- javax.xml.stream:stax-api:jar:1.0-2:compile
[INFO] |  +- org.codehaus.jackson:jackson-core-asl:jar:1.7.1:compile
[INFO] |  +- org.codehaus.jackson:jackson-mapper-asl:jar:1.7.1:compile
[INFO] |  +- org.codehaus.jackson:jackson-jaxrs:jar:1.7.1:compile
[INFO] |  \- org.codehaus.jackson:jackson-xc:jar:1.7.1:compile

因为Java 7带有自己的JAXB实现(事实上JAXB RI),所以我们不需要com.sun.xml.bind:jaxb-impl 包。将排除添加到pom.xml ::

的相应部分
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-json</artifactId>
        <version>${jersey.version}</version>
        <exclusions>
            <exclusion>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-impl</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

为了在测试中更快地达到结果,我减少了Tomcat内存::

JAVA_OPTS="-Djava.awt.headless=true -Xmx212m -XX:+UseConcMarkSweepGC -XX:MaxPermSize=66m"

重新开发/使用应用程序10次,不会为::

select x from org.apache.catalina.loader.WebappClassLoader x where x.state.name.toString() == "DESTROYED"

重新部署&#34; Visual GC&#34;插件显示PermGen清理。

使用以前的开发设置运行需要::

JAVA_OPTS="-Djava.awt.headless=true -Xmx512m -XX:+UseConcMarkSweepGC -XX:MaxPermSize=256m"

生存4-5重新部署。更大的PermGen的OQL查询给出了几个Tomcat的WebappClassLoader 但检查实例表明,没有GC的路径,当PermGen成为时,它们会被清除 满。