Case and analysis
Problem background
In Tomcat, the following code is inside the WebApp, causing a WebappClassLoader
leak that cannot be reclaimed.
public class MyCounter {private int count = 0;
public void Increment () {count++;
public int GetCount () {return count; } public class Mythreadlocal extends threadlocal<mycounter> {} public class Leakingservlet extends HttpServlet
{private static mythreadlocal mythreadlocal = new mythreadlocal (); protected void doget (HttpServletRequest request, httpservletresponse response) throws Servletexception, Ioexce
ption {MyCounter counter = Mythreadlocal.get ();
if (counter = = NULL) {counter = new MyCounter ();
Mythreadlocal.set (counter);
} response.getwriter (). println ("The current thread served this servlet" + counter.getcount ()
+ "Times");
Counter.increment (); }
}
The above code, as long as LeakingServlet
it is invoked once, and the thread that executes it does not stop, it can cause a WebappClassLoader
leak. Each time you reload
apply, there will be more than one WebappClassLoader
instance, which eventually results PermGen OutOfMemoryException
.
Solve the problem
Now let's think about why the above ThreadLocal
subclass causes a memory leak.
WebappClassLoader
First, we have to figure out WebappClassLoader
what the hell is going on?
For WEB applications running in the Java EE container, the ClassLoader is implemented differently than a typical Java application. Different WEB containers can be implemented differently. In the case of Apache Tomcat, each WEB application has a corresponding class loader instance. The class loader also uses proxy mode, but the difference is that it first tries to load a class, if no more proxies are found to the parent class loader. This is in contrast to the order of the generic class loader. This is the recommended practice in the Java Servlet specification, which is designed to make the Web apply its own class higher precedence than the Web container-supplied class. One exception to this proxy pattern is that the Java core library class is not within the lookup range. This is also to ensure that the Java core library is type-safe.
That WebappClassLoader
is, the custom class loader that Tomcat loads WebApp, each webapp ClassLoader is different, in order to isolate classes that are loaded by various applications.
So WebappClassLoader
what does the feature have to do with memory leaks? Not yet, but it's a very important feature that deserves our attention: every WebApp will have its own WebappClassLoader
, unlike the Java core ClassLoader.
We know that WebappClassLoader
the leak must be caused by a strong reference to another object, so we can try to draw their reference graph. Wait a minute! What is the role of the ClassLoader? Why are they strongly referenced?
Life cycle of classes and ClassLoader
To solve the problem above, we have to look at the life cycle of the class and the relationship of the ClassLoader.
The main related to our case is the uninstall of the class:
After the class has been used, the class is unloaded if the following conditions are true:
1, all instances of the class have been reclaimed, that is, no instances of the class exist in the Java heap.
2, loading the class ClassLoader
has been recycled.
3. The corresponding object of the class is java.lang.Class
not referenced anywhere, and there is no way to access the class by reflection anywhere.
If all three of these conditions are met, the JVM will unload the class when garbage collection in the method area, and the class unload process is to empty the class information in the method area, and the entire lifecycle of the Java class is over.
Classes loaded by the class loader brought by the Java virtual machine are never unloaded during the life cycle of the virtual machine. The Java Virtual machine's own class loader includes the root class loader, the extended class loader, and the System class loader. These class loaders are always referenced by the Java Virtual machine itself, and these class loaders always refer to the class objects of the classes they load, so these class objects are always accessible.
Classes loaded by the user-defined ClassLoader can be unloaded.
Note that the above remark, WebappClassLoader
if leaked, means that the classes it loads cannot be unloaded, which explains why the above code causes PermGen OutOfMemoryException
.
The key point looks at the picture below
we can see that the class loader object is doubly associated with the class object it loads. This means that the Class object may be a strong reference WebappClassLoader
, causing it to leak.
referencing graphs
After understanding the relationship between the ClassLoader and the life cycle of the class, we can begin to draw a reference graph. (in the picture LeakingServlet.class
and the myThreadLocal
reference painting is not rigorous, mainly to express the myThreadLocal
meaning of the class variable)
Below, we analyze the cause of the leak according to the above diagram WebappClassLoader
.
1, LeakingServlet
holding static
MyThreadLocal
, resulting myThreadLocal
in a life cycle LeakingServlet
as long as the class life cycle. This means that myThreadLocal
it will not be recycled, and weak references are useless, so when the frontline fails to pass ThreadLocalMap
the protective measures to clear counter strong references.
2, strong reference chain: thread -> threadLocalMap -> counter -> MyCounter.class -> WebappClassLocader
, leading to WebappClassLoader
leakage.
Summarize
Memory leaks are difficult to spot and are often caused by multiple causes. Threadlocal because of its thread-bound lifecycle as a frequent memory leak, a bit of carelessness breeds catastrophe. This article is only an analysis of a particular case, if it can be extrapolate, that is excellent. I hope this article can be helpful to everyone.