9

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

以前有人观察过这个问题吗?有什么建议(除了在应用程序关闭时使用反射使静态字段为空)?

4

2 回答 2

13

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

于 2012-07-06T11:24:47.493 回答
1

Tomcat 8 在开发过程中对后续 Web 应用程序重新部署发出警告:

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"

访问“instalce”选项卡中指向对象的指向链接允许在“参考部分”中调用“查找最近的 GC 根”并将文本表示复制到剪贴板:

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导致这个 SO 帖子。

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

$ 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"

在重新部署“Visual GC”插件下显示 PermGen 清理。

使用以前的开发设置运行需要一些东西::

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

生存 4-5 次重新部署。对较大 PermGen 的 OQL 查询给出了几个 Tomcat WebappClassLoader ,但检查实例表明没有通往 GC 的路径,并且当 PermGen 变满时它们会被清理。

于 2015-12-19T21:15:40.117 回答