Вопрос-ответ

To prevent a memory leak, the JDBC Driver has been forcibly unregistered

Для предотвращения утечки памяти драйвер JDBC был принудительно незарегистрирован

Я получаю это сообщение при запуске своего веб-приложения. Оно работает нормально, но я получаю это сообщение при завершении работы.


СЕРЬЕЗНАЯ ПРОБЛЕМА: веб-приложение зарегистрировало драйвер JBDC [oracle.jdbc.driver.OracleDriver], но не смогло отменить его регистрацию при остановке веб-приложения. Чтобы предотвратить утечку памяти, драйвер JDBC был принудительно незарегистрирован.


Приветствуется любая помощь.

Переведено автоматически
Ответ 1

Начиная с версии 6.0.24, Tomcat поставляется с функцией обнаружения утечки памяти, которая, в свою очередь, может приводить к такого рода предупреждающим сообщениям, когда в веб-приложении есть драйвер, совместимый с JDBC 4.0, /WEB-INF/lib который автоматически регистрируется при запуске веб-приложения с использованием ServiceLoader API, но который не автоматически отменяет регистрацию при завершении работы веб-приложения. Это сообщение носит чисто неофициальный характер, Tomcat уже предпринял соответствующие действия по предотвращению утечки памяти.

Что вы можете сделать?


  1. Игнорируйте эти предупреждения. Tomcat выполняет свою работу правильно. Фактическая ошибка находится в чужом коде (драйвере JDBC, о котором идет речь), а не в вашем. Порадуйтесь, что Tomcat выполнил свою работу должным образом, и подождите, пока поставщик драйвера JDBC исправит это, чтобы вы могли обновить драйвер. С другой стороны, вы не должны удалять драйвер JDBC в webapp /WEB-INF/lib, а только в server /lib. Если вы все еще храните его в webapp /WEB-INF/lib, то вам следует вручную зарегистрировать и отменить регистрацию с помощью ServletContextListener.



  2. Выполните обновление до Tomcat 6.0.23 или более поздней версии, чтобы вас не беспокоили эти предупреждения. Но при этом будет происходить утечка памяти. Не уверен, что это полезно знать в конце концов. Такого рода утечки памяти являются одной из основных причин OutOfMemoryError проблем при горячем развертывании Tomcat.



  3. Переместите драйвер 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;
}

}

Пожалуйста, не стесняйтесь комментировать и / или добавлять...

java tomcat jdbc