18

有没有办法解决缓存组件中的Google Guava r15内存泄漏(链接到错误报告) ?

(不依赖应用程序服务器可能会清理东西和/或考虑 Web 应用程序永远不会重新启动/重新部署)

4

3 回答 3

11

I guess you don't need to care about it. The Tomcat message says

Threads are going to be renewed over time to try and avoid a probable memory leak.

IIUIC it means that once all old threads are gone, so will all the pointers to the old version of your class.

Details: The reason for the thread pooling is the big cost of thread creation. The pooling itself is hacky as you get a thread which was doing something else and thread are not stateless. Thread creation is expensive assuming you'd need a lot of them and never recycle them. There's nothing wrong with renewing all threads every few minutes, so I hoped, Tomcat's workaround solves it perfectly. But it's not the case.

EDIT

I'm afraid, I misunderstood something. The linked bug says

It seems that web applications which are using guava cache might face a memory leak. After several redeployments, the application container crashes or stalls with an OutOfMemoryError.

I thought Tomcat could solve it easily, but for whatever reason it doesn't. So I'm afraid, you have to clean the ThreadLocals yourself. This is easily possible via reflection, the concerned fields are Thread.threadLocals and possibly inheritableThreadLocals. It's a bad hack and the harder part is to make this happen when nothing can go wrong, i.e., when no application is loaded.

EDIT 2 and 3

I guess it's safe to do something like

Stripped64.threadHashCode = new ThreadHashCode();

as the contained things are only needed for performance under heavy contention and they get recreated upon use. But according to MRalwasser's comment, it won't help at all as alive threads will still refer the old value. So there seem to be no way.

As ThreadLocal works by storing data with the threads (rather then using a real Map<Thread, Something>), you'd have to through all threads and remove references there. Fooling around with other threads' private fields is a terrible idea as they are not thread-safe and also due to visibility issues.

Another thing that might or mighn't not work is my proposal on the issue page. It's just a 20 line patch. Or simply wait, the issue has been assigned yesterday.

EDIT 4

Thread locals which don't get used can't cause any problems. AFAIK the only use of this TL is in cache stats. So avoid both CacheBuilder.recordStats and Cache.stats and Stripped64 won't get loaded.

EDIT 5

It looks like it's gonna get fixed finally. From the issue:

Doug fixed this upstream for us and we patched it back into Guava: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.9

At the first glance his change seems to be identical to mine.

EDIT 6

Finally, this has been marked as fixed and Guava 18.0-rc1 has been announced. It's just sad it took that long given that the change is the same as mine (9 month ago).

于 2013-10-28T23:45:30.903 回答
2

您可以使用 ServletListener ClassLoaderLeakPreventor https://github.com/mjiderhamn/classloader-leak-prevention/也可以在取消部署/停止时清除 ThreadLocals。它还具有其他常见泄漏的修复/解决方法。

于 2013-11-06T16:21:38.427 回答
0

似乎是 ThreadLocals 的一个缺点。每次将应用程序级别的类放入 ThreadLocal 时,都会得到相同的结果。

我猜唯一的解决方法是在部署时重新启动服务器。我认为这是 Java 应用程序的一个已知问题。你确定它是唯一停止卸载类加载器的地方吗?

于 2013-10-28T19:04:15.873 回答