有没有办法解决缓存组件中的Google Guava r15 memory leak (link to the bug report)?
(不依赖于应用程序服务器可能会清理和/或考虑永远不会重新启动/重新部署Web应用程序)
答案 0 :(得分:11)
我想你不需要关心它。 Tomcat消息说
线程会随着时间的推移而更新,以避免可能的内存泄漏。
IIUIC意味着一旦所有旧线程都消失了,所有指向旧版本的指针的指针也将消失。
详细信息:线程池的原因是线程创建的巨大成本。池化本身是hacky,因为你得到一个正在做其他事情的线程,而线程不是无状态的。假设您需要很多线程并且从不回收它们,线程创建是昂贵的。每隔几分钟更新所有线程没有任何问题,所以我希望,Tomcat的解决方法能够完美地解决它。 但事实并非如此。
我害怕,我误会了什么。链接的错误说
使用番石榴缓存的Web应用程序似乎可能面临内存泄漏。 在多次重新部署之后,应用程序容器因OutOfMemoryError崩溃或停止。
我认为Tomcat可以很容易地解决它,但无论出于什么原因它都没有。所以我很害怕,你必须自己清理ThreadLocal
。通过反射很容易实现,相关字段为Thread.threadLocals
,可能为inheritableThreadLocals
。这是一个糟糕的黑客,更难的部分是当没有任何错误时,即没有加载任何应用程序时实现这一点。
我认为做
之类的事情是安全的Stripped64.threadHashCode = new ThreadHashCode();
因为所包含的内容仅在重大争用下才能获得性能,并且在使用时会重新创建。 但根据MRalwasser的评论,它根本无济于事,因为活着的线程仍会引用旧值。所以似乎没办法。
由于ThreadLocal
通过使用线程存储数据(而不是使用真实的Map<Thread, Something>
)来工作,所以您必须通过所有线程并删除那里的引用。愚弄其他线程的私有字段是一个可怕的想法,因为它们不是线程安全的,也是由于可见性问题。
另一件可能或不可行的事情是我对issue page的建议。这只是一个20行补丁。或者只是等一下,问题已于昨天分配。
未使用的线程本地不会导致任何问题。 AFAIK唯一使用此TL的是缓存统计信息。因此,请避免同时CacheBuilder.recordStats
和Cache.stats
和Stripped64
加载。
看起来它最终会被修复。来自issue:
Doug为我们修复了这个问题,我们将它修补回了番石榴: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.9
乍一看his change似乎与mine相同。
最后,这已被标记为已修复,Guava 18.0-rc1已宣布。考虑到变化与我的变化(9个月前)相同,这很长很难过。
答案 1 :(得分:2)
您可以使用ServletListener ClassLoaderLeakPreventor https://github.com/mjiderhamn/classloader-leak-prevention/,它还会在取消部署/停止时清除ThreadLocals。它还有其他常见泄漏的修复/解决方法。
答案 2 :(得分:0)
似乎是ThreadLocals的缺点。每次在ThreadLocal中放置应用程序级别时,都会得到相同的结果。
唯一的解决方法是在部署时重新启动服务器。我认为这是Java应用程序的一个已知问题。你确定它是唯一一个停止卸载类加载器的地方吗?