JMX: Как предотвратить утечку памяти Classloader в контейнере сервлета?

Мне интересно, как и как я должен иметь дело с MBeans, которые прямо или косвенно регистрируются из моего приложения, которое развертывается в контейнере сервлетов.

В большинстве случаев есть два варианта получения MBeanServer, которые вы можете использовать для регистрации

  • создайте свой собственный MBeanServer с помощью MBeanServerFactory.createMBeanServer()

  • Используйте ManagementFactory.getPlatformMBeanServer()

При использовании первого варианта легко удалить все MBeans: Просто вызовите MBeanServer.releaseMBeanServer(myMBeanServer).

Но как насчет второго варианта, который часто используется во многих сторонних приложениях? (и BTW, это также рекомендуется для Sun/Oracle).

Поскольку платформа MBeanServer используется, она не будет отменена регистрации, если контекст сервлета будет уничтожен, но еще хуже, он все еще содержит ссылку на загрузчик классов веб-приложений.
Как следствие, все статические ссылки веб-приложения не будут выпущены, что приведет к утечке.

Если вам нравится протестировать это: просто разверните простое веб-приложение, которое выделяет массив 100 Мбайт, который является ссылкой статически и который использует драйвер oracle jdbc (он зарегистрирует диагностический MBean с использованием сервера mbean платформы), развернутого на tomcat. Остановите приложение и перезапустите его - повторите это, и вы нажмете OutOfMemoryError.

Вопросы:

  • Должен ли я иметь дело с этими проблемами вообще или это проблема контейнера сервлетов и/или сторонней библиотеки?

  • Есть ли способ получить все MBeans MBeanServer, какие классы загружаются определенным ClassLoader?

  • Что я могу сделать, чтобы предотвратить это? Должен ли я отслеживать все зарегистрированные MBeans на платформе MBeanServer и отменить регистрацию в течение contextDestroyed()?

Ответ 1

Что я могу сделать, чтобы предотвратить это? У меня есть должны отслеживать все зарегистрированные MBeans для платформы MBeanServer и отменить регистрацию в течение contextDestroyed()?

Это был мой стандартный совет. Я не знаю лучшего варианта.

Ответ 2

Я использую такую ​​злую стороннюю сторону. Чтобы обеспечить правильное выключение контекста сервлета, я перечисляю beans с помощью mbeanServer.queryMBeans(null, null), а затем unregisterMBean() beans, которые находятся в домене стороннего пользователя.

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

Ответ 3

Что говорит баклай. Кроме того, если вы используете фреймворк, такой как Spring (см. MBeanExporter), он должен позаботиться о том, чтобы отменить регистрацию объектов JMX при завершении контекста, что должно произойти как часть повторного развертывания webapp.