我有一个在生产环境中运行的基于SpringRoo的应用程序,在一些热重新部署后导致严重的permgen内存泄漏。
为了“找到并修复”泄漏并在分析过程中减少变量,我使用roo创建了一个简单,简化的应用程序,并获得了相同的行为。 该项目(使用Spring Roo(1.2.3.RELEASE)创建)仅使用名为“name”的字符串字段持久保存名为“Person”的实体。
我使用Oracle 11.2.0.2作为数据库在Tomcat 7.0.39上部署战争。 每次重新部署后,我都会在catalina.out
中收到此消息INFO: Undeploying context [/ojdbc-0.1.0.BUILD-SNAPSHOT]
mag 06, 2013 10:50:43 AM org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
SEVERE: The web application [/ojdbc-0.1.0.BUILD-SNAPSHOT] registered the JDBC driver [oracle.jdbc.OracleDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
经过两次热重新曝光后,我收到 permgen错误:
mag 06, 2013 10:51:08 AM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive /Applications/apache-tomcat-7.0.39/webapps/ojdbc- 0.1.0.BUILD-SNAPSHOT.war
Exception in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]"
Exception in thread "RMI TCP Connection(idle)" mag 06, 2013 10:51:17 AM ServerCommunicatorAdmin reqIncoming
WARNING: The server has decided to close this client connection.
java.lang.OutOfMemoryError: PermGen space
Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: PermGen space
我尝试使用VisualVm / EclipseMemory Analyzer进行分析,这是我到目前为止所做的。
事实是我没有观察到另一个数据库(例如PostgreSQL或Hypersonic)的这种行为。是否有与Oracle相关的问题导致泄漏?
Here是一个包含roo脚本生成器的zip存档,它是.hprof转储文件。
答案 0 :(得分:0)
尝试将Oracle JDBC驱动程序移动到Tomcat的lib目录中,而不是在应用程序的lib文件夹中。看起来OracleDiagnosabilityMBean正在处理Catalina。
编辑:由于您无法控制Tomcat,请尝试将Oracle类加载到哪里(除了替换Oracle init的AppContext):
http://cdivilly.wordpress.com/2012/04/23/permgen-memory-leak/
//somewhere in application startup, e.g. the ServletContextListener
try {
final ClassLoader active = Thread.currentThread().getContextClassLoader();
try {
//Find the root classloader
ClassLoader root = active;
while (root.getParent() != null) {
root = root.getParent();
}
//Temporarily make the root class loader the active class loader
Thread.currentThread().setContextClassLoader(root);
//Force the AppContext singleton to be created and initialized
sun.awt.AppContext.getAppContext();
} finally {
//restore the class loader
Thread.currentThread().setContextClassLoader(active);
}
} catch ( Throwable t) {
//Carry on if we get an error
LOG.warning("Failed to address PermGen leak");
}
答案 1 :(得分:0)
有一个内部Oracle错误报告
Oracle Support Bug 18707079 (JDBC THIN DRIVER LEAKS THREADS AND REFERENCES ON WEBAPP SHUTDOWN):
https://support.oracle.com/epmos/faces/BugDisplay?id=18707079
描述了您的问题:
When the webapp is shutdown, customer experience the following problems:
1) The OracleDiagnosabilityMBean is not de-registered
2) OracleTimeoutPollingThread does not stop
3) The following thread does not stop:
oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser
设置为Fixed in Product Version 12.2
。希望它很快就会发布!
答案 2 :(得分:0)
试试这个:
在web xml中注册ServletContextListener
<listener>
<listener-class>my.package.MyShutdownServletContextListener</listener-class>
</listener>
源:
public class MyShutdownServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
java.sql.DriverManager.getDrivers();
Class.forName("oracle.jdbc.driver.OracleTimeoutThreadPerVM");
} catch (ClassNotFoundException e) {
/* noop */
} finally {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
@Override
public void contextDestroyed(ServletContextEvent event) {}
}