Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JMX: How to prevent Classloader memory leaks in a servlet container?

I am wondering if or how I should deal with MBeans which are registered directly or indirectly from my application which gets deployed on a servlet container.

In most cases there are two options to retrieve a MBeanServer which you can use for registering

  • create your own MBeanServer using MBeanServerFactory.createMBeanServer()

  • Use ManagementFactory.getPlatformMBeanServer()

When using the first option, it's easy to deregister all MBeans: Just invoke MBeanServer.releaseMBeanServer(myMBeanServer).

But what about the second option which is used often in many 3rd party applications? (and BTW, this is also the recommended way from Sun/Oracle).

Because the platform MBeanServer is used, it won't be deregistered when the servlet context is destroyed - but even worse it still helds a reference to the web application classloader.
As a consequence all static references of the web application won't get released which results in a leak.

If you like to test this: Just deploy a simple web application which allocates a 100MB array which is references statically and which uses an oracle jdbc driver (it will register a diagnostic MBean using the platform mbean server), deployed on tomcat. Stop the application and restart it - repeat this, and you'll hit an OutOfMemoryError.

Questions:

  • Do I have to deal with these issues in general or is it a problem of the servlet container and/or the 3rd party library?

  • Is there a way to get all MBeans of an MBeanServer which classes are loaded by a specific ClassLoader?

  • What can I do to prevent this? Do I have to keep track of all registered MBeans to the platform MBeanServer and unregister it during contextDestroyed()?

like image 770
MRalwasser Avatar asked Jun 20 '11 18:06

MRalwasser


People also ask

Why is there a memory leak in Java?

They haven’t been cleaned up by the GC, explaining the memory leak: something is keeping these instances alive, along with all the data they contains. Before searching the reason of the memory leak, it is really important to understand how Java garbage collection works.

What causes OutOfMemoryError in Java?

The first thing we’ll need is a solid piece of code that causes OutOfMemoryError. OutOfMemoryError is an Exception thrown by JVM which informs us that we have less memory than we need. There could be many possible reasons why this Exception might be thrown and you can look at the cause of the Exception to see what’s going on.

How to find the source of heap DUMP error in JVM?

We can look inside JVM to find the source of the problem. Let’s add a -XX:+HeapDumpOnOutOfMemoryError flag which causes the Heap Dump to be generated on OutOfMemoryError. scala Application -J-Xmx10m -J-Xms10m -J-XX:+HeapDumpOnOutOfMemoryError


Video Answer


2 Answers

I'm using such an evil third-party. To ensure proper servlet context shutdown, I enumerate the beans using mbeanServer.queryMBeans(null, null) and then unregisterMBean() the beans which are in the domain of the third-party.

Set<ObjectInstance> beans = mbeanServer.queryMBeans(null, null);
for (ObjectInstance objectInstance : beans) {
    if (objectInstance.getObjectName().getDomain().equals("third-party-domain")) {
        try {
            mbeanServer.unregisterMBean(objectInstance.getObjectName());
        } catch (MBeanRegistrationException exception) {
            //error handling
        } catch (InstanceNotFoundException exception) {
            //error handling
        }
    }
}
like image 190
gawi Avatar answered Oct 21 '22 08:10

gawi


What can I do to prevent this? Do I have to keep track of all registered MBeans to the platform MBeanServer and unregister it during contextDestroyed()?

This has been my standard advice. I'm not aware of a better option.

like image 34
Brett Kail Avatar answered Oct 21 '22 08:10

Brett Kail