我知道这个问题已经存在至少3年(Issue 92),但我仍然不满意它的当前状态。我也知道如果你在重新部署后重新启动,这不会影响Tomcat(如Guice + Tomcat potential memory leak中所述)。
我的问题是我在重新部署后遇到OutOfMemoryError: PermGen
错误。请注意,我没有明确使用google-collections,我只使用Guice 3.0(通过maven)。在分析堆转储之后,我仍然看到线程com.google.inject.internal.Finalizer
仍处于活动状态,保留对Tomcat的WebappClassLoader的引用,从而阻止垃圾回收。
如果我实际上需要重新部署而不重新启动并使用Guice,该怎么办?我有什么选择?
答案 0 :(得分:9)
好吧,没有人帮助我,所以这就是我学到的东西:
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线程会正常死亡。