47

When I redeploy my application in tomcat, I get the following issue:

 The web application [] created a ThreadLocal with key of type
 [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@10d16b])
 and a value of type [com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty]
(value [com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty@1a183d2]) but 
 failed to remove it when the web application was stopped. 
 This is very likely to create a memory leak.

Also, am using ehcache in my application. This also seems to result in the following exception.

     SEVERE: The web application [] created a ThreadLocal with key of type [null] 
     (value [com.sun.xml.bind.v2.ClassFactory$1@24cdc7]) and a value of type [java
     .util.WeakHashMap... 

The ehcache seems to create a weak hash map and I get the message that this is very likely to create a memory leak.

I searched over the net and found this, http://jira.pentaho.com/browse/PRD-3616 but I dont have access to the server as such.

Please let me know if these warnings have any functional impact or can they be ignored? I used the "Find Memory leaks" option in tomcat manager and it says "No memory leaks found"

javanna
  • 59,145
  • 14
  • 144
  • 125
Raghav
  • 1,014
  • 2
  • 16
  • 34
  • 2
    The warnings mean that your ability to redeploy the application without restarting Tomcat itself is limited. Webapps have long been plagued by memory leaks of this type. They have no impact unless you redeploy the apps. I don't know, but I suspect these messages in Tomcat's output, which started showing up a year or two again, are to put pressure on framework builders to start cleaning up properly after themselves upon restart. – Ryan Stewart Oct 17 '11 at 00:26

5 Answers5

43

When you redeploy your application, Tomcat creates a new class loader. The old class loader must be garbage collected, otherwise you get a permgen memory leak.

Tomcat cannot check if the garbage collection will work or not, but it knows about several common points of failures. If the webapp class loader sets a ThreadLocal with an instance whose class was loaded by the webapp class loader itself, the servlet thread holds a reference to that instance. This means that the class loader will not be garbage collected.

Tomcat does a number of such detections, see here for more information. Cleaning thread locals is difficult, you would have to call remove() on the ThreadLocal in each of the threads that is was accessed from. In practice this is only important during development when you redeploy your web app multiple times. In production, you probably do not redeploy, so this can be ignored.

To really find out which instances define the thread locals, you have to use a profiler. For example the heap walker in JProfiler (disclaimer: my company develops JProfiler) will help you to find those thread locals. Select the reported value class (com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty or com.sun.xml.bind.v2.ClassFactory) and show the cumulated incoming references. One of those will be a java.lang.ThreadLocal$ThreadLocalMap$Entry. Select the referenced objects for that incoming reference type and switch to the allocations view. You will see where the instance has been allocated. With that information you can decide whether you can do something about it or not.

enter image description here

Ingo Kegel
  • 46,523
  • 10
  • 71
  • 102
  • 2
    As I see, it seems JProfiler is not a free,open-source solution. Can you suggest some other alternative – Raghav Oct 19 '11 at 19:34
  • 1
    @Raghav Eclipse Memory Analyzer is also great for analyzing heap dumps. http://www.eclipse.org/mat/ – Phil Jan 16 '14 at 19:55
8

Mattias Jiderhamn has an excellent 6-part article that explains very clearly the theory and practice about classloader leaks. Even better, he also released a jar file that we can include in our war files. I tried it on my web apps, and the jar file worked like a charm! The jar file is called classloader-leak-prevention.jar. To use it is as simple as just adding this to our web.xml

<listener>
  <listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor</listener-class>
</listener>

and then adding this to our pom.xml

<dependency>
  <groupId>se.jiderhamn</groupId>
  <artifactId>classloader-leak-prevention</artifactId>
  <version>1.15.2</version>
</dependency>

For more information, please refer to the project home page hosted on GitHub or Part 6 of his article

leeyuiwah
  • 6,562
  • 8
  • 41
  • 71
4

Creating Threads without cleaning them up correctly will eventually run you out of memory - been there, done that.

Those who are still wondering for quick solution/workaround, can go for below:

  • If running the standalone tomcat, kill javaw.exe or the process bearing it.
  • If running from eclipse, kill eclipse.exe and java.exe or enclosing process.
  • Still not resolved, Check for the task manager, it is likely that the process which is causing this will be shown with highest memory usage - Do your analysis and kill that.

You should be good to redeploy the stuff and proceed without memory issues.

Snehal Masne
  • 3,403
  • 3
  • 31
  • 51
2

I guesses you probably seen this but just in case ehcache doc recommends to put the lib in tomcat and not in WEB-INF/lib.

lfurini
  • 3,729
  • 4
  • 30
  • 48
user2177336
  • 205
  • 3
  • 13
1

I recommend initializing thread locals, in a ServletRequestListener.

ServletRequestListener has 2 methods: one for initialization and one for destruction.

This way, you can cleanup your ThreadLocal. Example:

public class ContextInitiator implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        context = new ThreadLocal<ContextThreadLocal>() {
            @Override
            protected ContextThreadLocal initialValue() {
                ContextThreadLocal context = new ContextThreadLocal();
                return context;
            }
        };
        context.get().setRequest(sre.getServletRequest());
    }
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        context.remove();
    }
}

web.xml:

<listener>
    <listener-class>ContextInitiator</listener-class>
</listener>
AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277