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()
?
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.
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.
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
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
}
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With