Для предотвращения утечки памяти драйвер JDBC был принудительно незарегистрирован
Я получаю это сообщение при запуске своего веб-приложения. Оно работает нормально, но я получаю это сообщение при завершении работы.
СЕРЬЕЗНАЯ ПРОБЛЕМА: веб-приложение зарегистрировало драйвер JBDC [oracle.jdbc.driver.OracleDriver], но не смогло отменить его регистрацию при остановке веб-приложения. Чтобы предотвратить утечку памяти, драйвер JDBC был принудительно незарегистрирован.
Приветствуется любая помощь.
Переведено автоматически
Ответ 1
Начиная с версии 6.0.24, Tomcat поставляется с функцией обнаружения утечки памяти, которая, в свою очередь, может приводить к такого рода предупреждающим сообщениям, когда в веб-приложении есть драйвер, совместимый с JDBC 4.0, /WEB-INF/lib
который автоматически регистрируется при запуске веб-приложения с использованием ServiceLoader
API, но который не автоматически отменяет регистрацию при завершении работы веб-приложения. Это сообщение носит чисто неофициальный характер, Tomcat уже предпринял соответствующие действия по предотвращению утечки памяти.
Что вы можете сделать?
Игнорируйте эти предупреждения. Tomcat выполняет свою работу правильно. Фактическая ошибка находится в чужом коде (драйвере JDBC, о котором идет речь), а не в вашем. Порадуйтесь, что Tomcat выполнил свою работу должным образом, и подождите, пока поставщик драйвера JDBC исправит это, чтобы вы могли обновить драйвер. С другой стороны, вы не должны удалять драйвер JDBC в webapp
/WEB-INF/lib
, а только в server/lib
. Если вы все еще храните его в webapp/WEB-INF/lib
, то вам следует вручную зарегистрировать и отменить регистрацию с помощьюServletContextListener
.Выполните обновление до Tomcat 6.0.23 или более поздней версии, чтобы вас не беспокоили эти предупреждения. Но при этом будет происходить утечка памяти. Не уверен, что это полезно знать в конце концов. Такого рода утечки памяти являются одной из основных причин
OutOfMemoryError
проблем при горячем развертывании Tomcat.Переместите драйвер JDBC в папку Tomcat
/lib
и используйте источник данных пула подключений для управления драйвером. Обратите внимание, что встроенный DBCP Tomcat не отменяет регистрацию драйверов должным образом при закрытии. Смотрите также ошибку DBCP-322, которая закрыта как WONTFIX . Вы бы предпочли заменить DBCP другим пулом соединений, который выполняет свою работу лучше, чем DBCP. Например , HikariCP или , возможно, Tomcat JDBC Pool.
Ответ 2
В вашем методе contextDestroyed() для прослушивания контекста сервлета отмените регистрацию драйверов вручную:
// This manually deregisters JDBC driver, which prevents Tomcat 7 from complaining about memory leaks wrto this class
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
LOG.log(Level.INFO, String.format("deregistering jdbc driver: %s", driver));
} catch (SQLException e) {
LOG.log(Level.SEVERE, String.format("Error deregistering driver %s", driver), e);
}
}
Ответ 3
Хотя Tomcat принудительно отменяет регистрацию драйвера JDBC для вас, тем не менее, хорошей практикой является очистка всех ресурсов, созданных вашим веб-приложением при уничтожении контекста, на случай, если вы перейдете в другой контейнер сервлета, который не выполняет проверки по предотвращению утечки памяти, которые выполняет Tomcat.
Однако методология полной отмены регистрации драйверов опасна. Некоторые драйверы, возвращаемые методом DriverManager.getDrivers()
, могли быть загружены родительским загрузчиком классов (т. Е. Загрузчиком классов контейнера сервлета), а не загрузчиком классов контекста веб-приложения (например, они могут находиться в папке lib контейнера, а не веб-приложения, и поэтому являются общими для всего контейнера). Отмена регистрации повлияет на любые другие веб-приложения, которые могут их использовать (или даже на сам контейнер).
Поэтому следует проверить, что загрузчик классов для каждого драйвера является загрузчиком классов веб-приложения, прежде чем отменять его регистрацию. Итак, в вашем методе contextDestroyed() ContextListener:
public final void contextDestroyed(ServletContextEvent sce) {
// ... First close any background tasks which may be using the DB ...
// ... Then close any DB connection pools ...
// Now deregister JDBC drivers in this context's ClassLoader:
// Get the webapp's ClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// Loop through all drivers
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
if (driver.getClass().getClassLoader() == cl) {
// This driver was registered by the webapp's ClassLoader, so deregister it:
try {
log.info("Deregistering JDBC driver {}", driver);
DriverManager.deregisterDriver(driver);
} catch (SQLException ex) {
log.error("Error deregistering JDBC driver {}", driver, ex);
}
} else {
// driver was not registered by the webapp's ClassLoader and may be in use elsewhere
log.trace("Not deregistering JDBC driver {} as it does not belong to this webapp's ClassLoader", driver);
}
}
}
Ответ 4
Я вижу, что эта проблема часто возникает. Да, Tomcat 7 автоматически отменяет его регистрацию, но это ДЕЙСТВИТЕЛЬНО берет под контроль ваш код и является хорошей практикой кодирования? Наверняка ВЫ хотите знать, что у вас есть весь правильный код для закрытия всех ваших объектов, завершения работы потоков пула подключений к базе данных и удаления всех предупреждений. Я, конечно, хочу.
Вот как я это делаю.
Шаг 1: Зарегистрируйте прослушиватель
web.xml
<listener>
<listener-class>com.mysite.MySpecialListener</listener-class>
</listener>
Шаг 2: Реализация прослушивателя
com.mysite.MySpecialListener.java
public class MySpecialListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// On Application Startup, please…
// Usually I'll make a singleton in here, set up my pool, etc.
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// On Application Shutdown, please…
// 1. Go fetch that DataSource
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource datasource = (DataSource)envContext.lookup("jdbc/database");
// 2. Deregister Driver
try {
java.sql.Driver mySqlDriver = DriverManager.getDriver("jdbc:mysql://localhost:3306/");
DriverManager.deregisterDriver(mySqlDriver);
} catch (SQLException ex) {
logger.info("Could not deregister driver:".concat(ex.getMessage()));
}
// 3. For added safety, remove the reference to dataSource for GC to enjoy.
dataSource = null;
}
}
Пожалуйста, не стесняйтесь комментировать и / или добавлять...