【问题标题】:Guice 3.0 + Tomcat 7.0 = ClassLoader memory leakGuice 3.0 + Tomcat 7.0 = ClassLoader 内存泄漏
【发布时间】:2012-02-09 03:58:33
【问题描述】:
我知道这个问题已经存在了至少 3 年 (Issue 92),但我仍然对它的当前状态不满意。我也知道,如果您在重新部署后重新启动(如Guice + Tomcat potential memory leak 中所建议的那样),这不会影响 Tomcat。
我的问题是我在重新部署后遇到了OutOfMemoryError: PermGen 错误。请注意,我没有明确使用 google-collections,我只使用 Guice 3.0(通过 maven)。分析堆转储后,我仍然看到线程com.google.inject.internal.Finalizer 仍然处于活动状态,保持对Tomcat 的WebappClassLoader 的引用,从而阻碍了垃圾回收。
如果我实际上需要重新部署而不重新启动并且正在使用 Guice,该怎么办?我有哪些选择?
【问题讨论】:
标签:
java
tomcat
memory-leaks
guice
【解决方案1】:
好吧,没有人可以帮助我,所以这是我学到的:
Finalizer 线程由 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();
}
完成此操作后,Finalizer 线程优雅地终止。