Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Critical guava memory leak - Workaround needed

Is there any way to workaround the Google Guava r15 memory leak (link to the bug report) in the cache component?

(Without relying that the application server might clean things up and/or considering that the web application will never be restarted/redeployed)

like image 512
Don Miller Avatar asked Oct 28 '13 18:10

Don Miller


People also ask

What is used for avoiding memory leakage in Java?

Use reference objects to avoid memory leaks Using the java. lang. ref package, you can work with the garbage collector in your program. This allows you to avoid directly referencing objects and use special reference objects that the garbage collector easily clears.


2 Answers

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).

like image 107
maaartinus Avatar answered Oct 28 '22 22:10

maaartinus


You can use the ServletListener ClassLoaderLeakPreventor https://github.com/mjiderhamn/classloader-leak-prevention/ which also clears ThreadLocals on undeploy/stop. It also has fixes/workarounds for other common leaks.

like image 26
Leonard Brünings Avatar answered Oct 28 '22 22:10

Leonard Brünings