一尘不染

Guice 3.0 + Tomcat 7.0 = ClassLoader内存泄漏

tomcat

我知道这个问题已经存在至少3年了(第92期),但是我仍然不满意它的当前状态。我还知道,如果您在重新部署后重新启动,这不会影响Tomcat(如Guice +Tomcat潜在的内存泄漏所述)。

我的问题是OutOfMemoryError: PermGen重新部署后遇到错误。请注意,我没有显式使用google-
collections,而是仅使用Guice
3.0(通过Maven)。在分析堆转储之后,我仍然看到该线程com.google.inject.internal.Finalizer仍处于活动状态,并保留了对Tomcat的WebappClassLoader的引用,从而阻碍了垃圾回收。

如果我 实际上需要 重新部署而不重新启动并且正在使用Guice,该怎么办?我有什么选择?


阅读 256

收藏
2020-06-16

共1个答案

一尘不染

好吧,没有人在那里帮助我,所以这是我学到的东西:

终结器线程由FinalizableReferenceQueue(FRQ)启动。MapMaker中有对FRQ的硬(静态)引用。WebAppClassLoader未被垃圾收集,因为由于硬引用,MapMaper仍然存在。

以下代码 解决了 我的问题:

final Class<?> queueHolderClass = 
    Class.forName("com.google.inject.internal.util.$MapMaker$QueueHolder");
final Field queueField = queueHolderClass.getDeclaredField("queue");
// make MapMaker.QueueHolder.queue accessible
queueField.setAccessible(true);
// remove the final modifier from MapMaker.QueueHolder.queue
final Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(queueField, queueField.getModifiers() & ~Modifier.FINAL);
// set it to null
queueField.set(null, null);

这是 令人反感的 代码(com.google.inject.internal.util.MapMaker):

/** Wrapper class ensures that queue isn't created until it's used. */
private static class QueueHolder {
  static final FinalizableReferenceQueue queue = new FinalizableReferenceQueue();
}

完成此操作后,终结器线程正常退出。

2020-06-16