JMX:如何防止 servlet 容器中的类加载器内存泄漏?

发布于 2024-11-16 11:04:55 字数 1145 浏览 2 评论 0原文

我想知道是否或如何处理从部署在 servlet 容器上的应用程序直接或间接注册的 MBean。

在大多数情况下,有两个选项可以检索 MBeanServer,您可以使用它们来注册

  • 使用 MBeanServerFactory.createMBeanServer() 创建自己的 MBeanServer >

  • 使用ManagementFactory.getPlatformMBeanServer()

使用第一个选项时,可以轻松取消注册所有 MBean: 只需调用 MBeanServer.releaseMBeanServer(myMBeanServer)

但是许多第三方应用程序中经常使用的第二个选项又如何呢? (顺便说一句,这也是 Sun/Oracle 推荐的方式)。

由于使用了 MBeanServer 平台,因此当 servlet 上下文被销毁时,它不会被取消注册 - 但更糟糕的是,它仍然保留对 Web 应用程序类加载器的引用。
因此Web 应用程序的所有静态引用都不会被释放,这会导致泄漏。

如果您想测试一下:只需部署一个简单的 Web 应用程序,该应用程序分配一个 100MB 的数组,该数组是静态引用并使用 oracle jdbc司机(它将使用平台 mbean 服务器注册一个诊断 MBean),部署在 tomcat 上。停止应用程序并重新启动 - 重复此操作,您将遇到 OutOfMemoryError

问题:

  • 我是否必须处理这些一般问题,还是 servlet 容器和/或第 3 方库的问题?

  • 有没有办法获取 MBeanServer 的所有 MBean,这些 MBean 是由特定 ClassLoader 加载的?

  • 我可以采取什么措施来防止这种情况发生?我是否必须跟踪所有注册到平台 MBeanServer 的 MBean 并在 contextDestroyed() 期间取消注册?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

墨小墨 2024-11-23 11:04:55

我正在使用如此邪恶的第三方。为了确保正确关闭 servlet 上下文,我使用 mbeanServer.queryMBeans(null, null) 枚举 bean,然后使用 unregisterMBean() 枚举位于第三个域中的 bean派对。

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
        }
    }
}

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
        }
    }
}
も星光 2024-11-23 11:04:55

我可以采取什么措施来防止这种情况发生?我是吗
必须跟踪所有注册的
MBeans 到平台 MBeanServer 和
期间取消注册
contextDestroyed()?

这是我的标准建议。我不知道有更好的选择。

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.

红焚 2024-11-23 11:04:55

bkail 在说什么。此外,如果您使用 Spring 等框架(请参阅 MBeanExporter),它应该在 Context 关闭时负责取消注册 JMX 对象,这应该作为重新部署 Web 应用程序的一部分进行。

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.

七秒鱼° 2024-11-23 11:04:55

同时你可以获取所有的Mbean,并检查它是否被容器类加载器加载,然后注销它。
示例代码是:

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);
 }
}

我建议您阅读以下项目 classloader-leak-prevention github。
该项目有一系列方法来处理容器中的类加载器泄漏。
对于这个问题,你可以看到 MBeanCleanUp.java

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文