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

4

10

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()?

Reimer answered 20/6, 2011 at 18:40 Comment(1)
Maybe have a look at #387392Triciatrick
F
1

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.

Floodlight answered 20/6, 2011 at 19:4 Comment(0)
I
3

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
        }
    }
}
Instance answered 16/5, 2013 at 1:53 Comment(1)
I'd also only unregister MBeans that was loaded by the web application's classloader: if (mbeanServer.getClassLoaderFor(objectInstance.getObjectName() == Thread.currentThread().getContextClassLoader())Wage
F
1

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.

Floodlight answered 20/6, 2011 at 19:4 Comment(0)
K
0

What bkail is saying. Also if you're using framework such as Spring (see MBeanExporter) it should take care of unregistering your JMX objects upon Context shutdown, which should happen as part of redeploy of the webapp.

Klipspringer answered 21/6, 2011 at 16:50 Comment(0)
C
0

while you can get all the Mbeans,and check if it was loadered by the container classloader,and then unregist it. the Sample code is:

final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
final Set<ObjectName> allMBeanNames = mBeanServer.queryNames(new ObjectName("*:*"), null);
for(ObjectName objectName : allMBeanNames) {
  final ClassLoader mBeanClassLoader = mBeanServer.getClassLoaderFor(objectName);
  if(containnerClasssloader.isClassLoaderOrChild(mBeanClassLoader)) { // MBean loaded by containnerClasssloader  
        mBeanServer.unregisterMBean(objectName);
 }
}

I recommend you to read the project classloader-leak-prevention from github. the project have a series of method to deal with classloader leak from containers. for this question,you can see the MBeanCleanUp.java

Clavicorn answered 5/11, 2018 at 1:8 Comment(4)
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewDelanos
because i'm not the author.I think post the content is not a good ideaClavicorn
Well, there is an option for reviewers to choose, Link-only answers will be deleted by stackoverflow later, so suggest you to add some essential things to your post, no need the whole code. Just like the site said Link-only answers can become invalid if the linked page changes. I think the site just want to make the answer always useful for all readers, no dependent on other site.Delanos
ok,i modify the answer,add the main code to explain the way to slove the qu.Clavicorn

© 2022 - 2024 — McMap. All rights reserved.