Java 为防止内存泄漏,JDBC Driver 已被强制注销
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3320400/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
To prevent a memory leak, the JDBC Driver has been forcibly unregistered
提问by mona
I am getting this message when I run my web application. It runs fine but I get this message during shutdown.
我在运行 Web 应用程序时收到此消息。它运行良好,但我在关机期间收到此消息。
SEVERE: A web application registered the JBDC driver [oracle.jdbc.driver.OracleDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
严重:Web 应用程序注册了 JBDC 驱动程序 [oracle.jdbc.driver.OracleDriver],但在 Web 应用程序停止时未能取消注册。为防止内存泄漏,JDBC Driver 已被强制注销。
Any help appreciated.
任何帮助表示赞赏。
采纳答案by BalusC
Since version 6.0.24, Tomcat ships with a memory leak detectionfeature, which in turn can lead to this kind of warning messages when there's a JDBC 4.0 compatible driver in the webapp's /WEB-INF/lib
which auto-registersitself during webapp's startup using the ServiceLoader
API, but which did not auto-deregisteritself during webapp's shutdown. This message is purely informal, Tomcat has already taken the memory leak prevention action accordingly.
从 6.0.24 版本开始,Tomcat 附带了内存泄漏检测功能,当 webapp 中存在 JDBC 4.0 兼容驱动程序时,这会导致此类警告消息,该驱动程序在webapp 启动期间使用API/WEB-INF/lib
自动注册,但是在 webapp 关闭期间没有自动注销。此消息纯属非正式,Tomcat 已经相应地采取了防止内存泄漏的措施。ServiceLoader
What can you do?
你能做什么?
Ignore those warnings. Tomcat is doing its job right. The actual bug is in someone else's code (the JDBC driver in question), not in yours. Be happy that Tomcat did its job properly and wait until the JDBC driver vendor get it fixed so that you can upgrade the driver. On the other hand, you aren't supposed to drop a JDBC driver in webapp's
/WEB-INF/lib
, but only in server's/lib
. If you still keep it in webapp's/WEB-INF/lib
, then you should manually register and deregister it using aServletContextListener
.Downgrade to Tomcat 6.0.23 or older so that you will not be bothered with those warnings. But it will silently keep leaking memory. Not sure if that's good to know after all. Those kind of memory leaks are one of the major causes behind
OutOfMemoryError
issuesduring Tomcat hotdeployments.Move the JDBC driver to Tomcat's
/lib
folder and have a connection pooled datasource to manage the driver. Note that Tomcat's builtin DBCP does not deregister drivers properly on close. See also bug DBCP-322which is closed as WONTFIX. You would rather like to replace DBCP by another connection pool which is doing its job better then DBCP. For example HikariCP, BoneCP, or perhaps Tomcat JDBC Pool.
忽略这些警告。Tomcat 的工作是正确的。实际的错误在其他人的代码(有问题的 JDBC 驱动程序)中,而不是在您的代码中。很高兴 Tomcat 正确地完成了它的工作,并等待 JDBC 驱动程序供应商修复它,以便您可以升级驱动程序。另一方面,您不应该在 webapp 中删除 JDBC 驱动程序
/WEB-INF/lib
,而只能在服务器的/lib
. 如果您仍然将它保留在 webapp 的 中/WEB-INF/lib
,那么您应该使用ServletContextListener
.降级到 Tomcat 6.0.23 或更早版本,这样您就不会被这些警告所困扰。但它会默默地不断泄漏内存。不知道这毕竟是不是很好。这类内存泄漏是Tomcat 热部署期间出现
OutOfMemoryError
问题的主要原因之一。将 JDBC 驱动程序移动到 Tomcat 的
/lib
文件夹,并有一个连接池数据源来管理驱动程序。请注意,Tomcat 的内置 DBCP 在关闭时不会正确注销驱动程序。另请参阅作为 WONTFIX 关闭的错误DBCP-322。您更愿意将 DBCP 替换为另一个比 DBCP 做得更好的连接池。例如HikariCP、BoneCP或Tomcat JDBC Pool。
回答by sick old bastard
This is purely driver registration/deregistration issue in mysql`s driver or tomcats webapp-classloader. Copy mysql driver into tomcats lib folder (so its loaded by jvm directly, not by tomcat), and message will be gone. That makes mysql jdbc driver to be unloaded only at JVM shutdown, and noone cares about memory leaks then.
这纯粹是 mysql 的驱动程序或 tomcats webapp-classloader 中的驱动程序注册/注销问题。将mysql驱动复制到tomcats lib文件夹中(所以它直接由jvm加载,而不是由tomcat加载),消息将消失。这使得 mysql jdbc 驱动程序只能在 JVM 关闭时卸载,然后没有人关心内存泄漏。
回答by Francisco Alvarado
I was having a similar problem, but additionally I was getting a Java Heap Space error anytime I modified/saved JSP pages with Tomcat server running, therefore the context were not fully recharged.
我遇到了类似的问题,但另外,每当我在运行 Tomcat 服务器的情况下修改/保存 JSP 页面时,我都会收到 Java 堆空间错误,因此上下文没有完全充电。
My versions were Apache Tomcat 6.0.29 and JDK 6u12.
我的版本是 Apache Tomcat 6.0.29 和 JDK 6u12。
Upgrading JDK to 6u21as suggested in Referencessection of URL http://wiki.apache.org/tomcat/MemoryLeakProtectionsolved the Java Heap Space problem (context now reloads OK) although JDBC Driver error still appears.
按照URL http://wiki.apache.org/tomcat/MemoryLeakProtection 的参考部分中的建议将 JDK 升级到6u21解决了 Java 堆空间问题(上下文现在重新加载正常),尽管 JDBC 驱动程序错误仍然出现。
回答by ae6rt
In your servlet context listener contextDestroyed() method, manually deregister the drivers:
在您的 servlet 上下文侦听器 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);
}
}
回答by Collin Peters
I will add to this something I found on the Spring forums. If you move your JDBC driver jar to the tomcat lib folder, instead of deploying it with your webapp, the warning seems to disappear. I can confirm that this worked for me
我会添加一些我在 Spring 论坛上找到的东西。如果您将 JDBC 驱动程序 jar 移动到 tomcat lib 文件夹,而不是使用您的 web 应用程序部署它,警告似乎消失了。我可以确认这对我有用
回答by sparkyspider
I see this issue come up a lot. Yes, Tomcat 7 does automatically deregister it, but it that REALLY taking control of your code and a good coding practice? Surely YOU want to know that you have all the correct code in place to close all your objects, shut down database connection pool threads, and get rid of all warnings. I certainly do.
我看到这个问题出现了很多。是的,Tomcat 7 会自动注销它,但它真的可以控制您的代码和良好的编码习惯吗?您肯定想知道您拥有关闭所有对象、关闭数据库连接池线程和消除所有警告的所有正确代码。我当然知道。
This is how I do it.
我就是这样做的。
Step 1: Register a Listener
第 1 步:注册监听器
web.xml
网页.xml
<listener>
<listener-class>com.mysite.MySpecialListener</listener-class>
</listener>
Step 2: Implement the Listener
第二步:实现监听器
com.mysite.MySpecialListener.java
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;
}
}
Please feel free to comment and/or add...
请随时发表评论和/或添加...
回答by Darrin M. Gorski
I found that implementing a simple destroy() method to de-register any JDBC drivers works nicely.
我发现实现一个简单的 destroy() 方法来取消注册任何 JDBC 驱动程序效果很好。
/**
* Destroys the servlet cleanly by unloading JDBC drivers.
*
* @see javax.servlet.GenericServlet#destroy()
*/
public void destroy() {
String prefix = getClass().getSimpleName() +" destroy() ";
ServletContext ctx = getServletContext();
try {
Enumeration<Driver> drivers = DriverManager.getDrivers();
while(drivers.hasMoreElements()) {
DriverManager.deregisterDriver(drivers.nextElement());
}
} catch(Exception e) {
ctx.log(prefix + "Exception caught while deregistering JDBC drivers", e);
}
ctx.log(prefix + "complete");
}
回答by Bharat
I found the same issue with Tomcat version 6.026.
我在 Tomcat 6.026 版中发现了同样的问题。
I used the Mysql JDBC.jar in WebAPP Library as well as in TOMCAT Lib.
我在 WebAPP 库和 TOMCAT 库中使用了 Mysql JDBC.jar。
To fix the above by removing the Jar from the TOMCAT lib folder.
通过从 TOMCAT lib 文件夹中删除 Jar 来解决上述问题。
So what I understand is that TOMCAT is handling the JDBC memory leak properly. But if the MYSQL Jdbc jar is duplicated in WebApp and Tomcat Lib, Tomcat will only be able to handle the jar present in the Tomcat Lib folder.
所以我的理解是 TOMCAT 正在正确处理 JDBC 内存泄漏。但是,如果 MYSQL Jdbc jar 在 WebApp 和 Tomcat Lib 中重复,则 Tomcat 将只能处理 Tomcat Lib 文件夹中存在的 jar。
回答by TimP
If you are getting this message from a Maven built war change the scope of the JDBC driver to provided, and put a copy of it in the lib directory. Like this:
如果您从 Maven 构建的War中收到此消息,请将 JDBC 驱动程序的范围更改为提供,并将其副本放在 lib 目录中。像这样:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
<!-- put a copy in /usr/share/tomcat7/lib -->
<scope>provided</scope>
</dependency>
回答by Andrea Ratto
Solution for per-app deployments
每个应用程序部署的解决方案
This is a listener I wrote to solve the problem: it autodetects if the driver has registered itself and acts accordingly.it
这是我为解决问题而编写的侦听器:它会自动检测驱动程序是否已自行注册并采取相应措施。
Important: it is meant to be used ONLY when the driver jar is deployed in WEB-INF/lib, not in the Tomcat /lib, as many suggest, so that each application can take care of its own driver and run on a untouched Tomcat. That is the way it should be IMHO.
重要提示:它意味着仅在驱动程序 jar 部署在 WEB-INF/lib 中时使用,而不是在 Tomcat /lib 中,正如许多人建议的那样,以便每个应用程序都可以处理自己的驱动程序并在未受影响的 Tomcat 上运行. 恕我直言,这就是它应该的方式。
Just configure the listener in your web.xml before any other and enjoy.
只需在您的 web.xml 中配置侦听器,然后享受。
add near the top of web.xml:
在web.xml顶部附近添加:
<listener>
<listener-class>utils.db.OjdbcDriverRegistrationListener</listener-class>
</listener>
save as utils/db/OjdbcDriverRegistrationListener.java:
另存为utils/db/OjdbcDriverRegistrationListener.java:
package utils.db;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import oracle.jdbc.OracleDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Registers and unregisters the Oracle JDBC driver.
*
* Use only when the ojdbc jar is deployed inside the webapp (not as an
* appserver lib)
*/
public class OjdbcDriverRegistrationListener implements ServletContextListener {
private static final Logger LOG = LoggerFactory
.getLogger(OjdbcDriverRegistrationListener.class);
private Driver driver = null;
/**
* Registers the Oracle JDBC driver
*/
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
this.driver = new OracleDriver(); // load and instantiate the class
boolean skipRegistration = false;
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
if (driver instanceof OracleDriver) {
OracleDriver alreadyRegistered = (OracleDriver) driver;
if (alreadyRegistered.getClass() == this.driver.getClass()) {
// same class in the VM already registered itself
skipRegistration = true;
this.driver = alreadyRegistered;
break;
}
}
}
try {
if (!skipRegistration) {
DriverManager.registerDriver(driver);
} else {
LOG.debug("driver was registered automatically");
}
LOG.info(String.format("registered jdbc driver: %s v%d.%d", driver,
driver.getMajorVersion(), driver.getMinorVersion()));
} catch (SQLException e) {
LOG.error(
"Error registering oracle driver: " +
"database connectivity might be unavailable!",
e);
throw new RuntimeException(e);
}
}
/**
* Deregisters JDBC driver
*
* Prevents Tomcat 7 from complaining about memory leaks.
*/
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
if (this.driver != null) {
try {
DriverManager.deregisterDriver(driver);
LOG.info(String.format("deregistering jdbc driver: %s", driver));
} catch (SQLException e) {
LOG.warn(
String.format("Error deregistering driver %s", driver),
e);
}
this.driver = null;
} else {
LOG.warn("No driver to deregister");
}
}
}