Java JMX:如何防止servlet容器中的类加载器内存泄漏?
我想知道是否应该或如何处理直接或间接从部署在servlet容器上的应用程序注册的MBean 在大多数情况下,有两个选项可用于检索可用于注册的Java JMX:如何防止servlet容器中的类加载器内存泄漏?,java,servlets,memory-leaks,classloader,jmx,Java,Servlets,Memory Leaks,Classloader,Jmx,我想知道是否应该或如何处理直接或间接从部署在servlet容器上的应用程序注册的MBean 在大多数情况下,有两个选项可用于检索可用于注册的MBeanServer 使用MBeanServerFactory.createMBeanServer() 使用ManagementFactory.getPlatformMBeanServer() 使用第一个选项时,可以轻松取消所有MBean的注册: 只需调用MBeanServer.releaseMeanServer(myMBeanServer) 但是,第
MBeanServer
- 使用
MBeanServerFactory.createMBeanServer()
- 使用
ManagementFactory.getPlatformMBeanServer()
MBeanServer.releaseMeanServer(myMBeanServer)
但是,第二个选项在许多第三方应用程序中经常使用,这又如何呢?
(顺便说一句,这也是Sun/Oracle推荐的方法)
因为使用了平台MBeanServer
,所以当servlet上下文被破坏时,它不会被取消注册-但更糟糕的是,它仍然会保存对web应用程序类加载器的引用。因此,不会释放web应用程序的所有静态引用,从而导致泄漏。 如果您想测试这一点:只需部署一个简单的web应用程序,该应用程序分配一个静态引用的100MB数组,并使用一个oracle jdbc驱动程序(它将使用平台MBean服务器注册一个诊断MBean),部署在tomcat上。停止应用程序并重新启动它-重复此操作,您将遇到
OutOfMemoryError
问题:
- 我是否必须处理这些问题,还是servlet容器和/或第三方库的问题
- 有没有一种方法可以获取
哪些类是由特定的MBeanServer的所有MBean
类加载器加载的
- 我能做些什么来防止这种情况?我是否必须跟踪所有注册到平台
的MBean,并在MBeanServer
期间注销它contextdestromed()
这是我的标准建议。我不知道还有更好的选择。bkail在说什么。另外,如果您使用的是Spring之类的框架(请参阅MBeanExporter),它应该在上下文关闭时注销JMX对象,这应该作为重新部署webapp的一部分来完成。我使用的是这样一个邪恶的第三方。为了确保正确关闭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
}
}
}
Set bean=mbeanServer.queryMBeans(null,null);
for(ObjectInstance ObjectInstance:beans){
if(objectInstance.getObjectName().getDomain().equals(“第三方域”)){
试一试{
mbeanServer.unregisterMBean(objectInstance.getObjectName());
}捕获(MBeanRegistrationException异常){
//错误处理
}捕获(InstanceNotFoundException异常){
//错误处理
}
}
}
而您可以获取所有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);
}
}
final MBeanServer MBeanServer=ManagementFactory.getPlatformMBeanServer();
最终设置allMBeanNames=mBeanServer.queryNames(新对象名(“*:*”),null);
for(ObjectName ObjectName:allMBeanNames){
最终类加载器mBeanClassLoader=mBeanServer.getClassLoaderFor(objectName);
if(containerClasssLoader.isClassLoaderArchild(mBeanClassLoader)){//MBean由containerClasssLoader加载
mBeanServer.unregisterMBean(objectName);
}
}
我建议您阅读github的项目。
该项目有一系列方法来处理容器中的类加载器泄漏。
对于这个问题,您可以看到“我也只注销web应用程序的类加载器加载的MBean:
如果(mbeanServer.GetClassLoader for(objectInstance.getObjectName()==Thread.currentThread().getContextClassLoader())
虽然此链接可以回答问题,但最好在此处包含答案的基本部分,并提供链接供参考。如果链接页面发生更改,仅链接的答案可能会无效。-因为我不是作者。我认为发布内容不是一个好主意。不过,有一个选项供评论者选择,链接打开stackoverflow将删除所有答案,因此建议您在帖子中添加一些必要的内容,不需要全部代码。正如网站所说,如果链接页面发生更改,只有链接答案可能无效。我认为网站只是想让答案对所有读者都有用,而不依赖其他网站。好的,我修改了答案,添加了主代码来解释解决问题的方法。