使用 Spring 的 LocalSessionFactoryBean 时出现 Permgen 问题
Permgen issue when using Spring's LocalSessionFactoryBean
@Configuration
public class DataSourceConfiguration
{
@Autowired
private Environment env;
@Bean(destroyMethod = "close")
public ComboPooledDataSource dataSource()
{
String datasourcePathStartsWith = "datasource.";
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try
{
dataSource.setContextClassLoaderSource("library");
dataSource.setPrivilegeSpawnedThreads(true);
dataSource.setDriverClass("oracle.jdbc.driver.OracleDriver");
dataSource.setJdbcUrl(env.getProperty(datasourcePathStartsWith + "url"));
dataSource.setUser(env.getProperty(datasourcePathStartsWith + "user"));
dataSource.setPassword(env.getProperty(datasourcePathStartsWith + "password"));
dataSource.setMinPoolSize(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "minPoolSize")));
dataSource.setMaxPoolSize(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "maxPoolSize")));
dataSource.setCheckoutTimeout(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "checkoutTimeout")));
dataSource.setMaxIdleTime(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "maxIdleTime")));
dataSource.setMaxStatements(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "maxStatements")));
}
catch (PropertyVetoException e)
{
e.printStackTrace();
}
return dataSource;
}
}
这是我的代码。它在 Tomcat 上工作 "fine" 7 - 当我多次重新部署应用程序并使用 "Find leaks" 功能时,它什么也没显示。但是如果我添加 sessionFactory,每次重新部署都会出现问题:
以下 Web 应用程序已停止(重新加载、取消部署),但它们的
类 之前运行的仍然加载在内存中,因此导致内存
泄漏(使用分析器确认):
/testPermGen
/testPermGen
/testPermGen
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource)
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
return sessionFactoryBean;
}
我试过这段代码,没有连接到数据源,没有成功。看起来问题出在应用程序停止时未关闭的 LocalSessionFactoryBean。
@Bean(destroyMethod = "destroy")
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
return sessionFactoryBean;
}
Tomcat 是 运行 以下标志:
-XX:MaxPermSize=128m
-XX:+UseConcMarkSweepGC
-XX:+CMSClassUnloadingEnabled
-XX:+CMSPermGenSweepingEnabled
是否可以解决此内存泄漏问题?
更新:
我已将整个项目(4 个文件)上传到 GitHub 以重现内存泄漏:https://github.com/anton-09/TestPermGen
Mattias Jiderhamn 的 classloader-leak-prevention 没有多大帮助,我在 Tomcat 7:
июл 03, 2017 11:44:27 AM se.jiderhamn.classloader.leak.prevention.JULLogger warn
WARNING: Waiting for Thread 'Thread[Resource Destroyer in BasicResourcePool.close(),5,main]' of type com.mchange.v2.resourcepool.BasicResourcePool loaded by protected ClassLoader with contextClassLoader = protected ClassLoader or child for 5000 ms. Thread stack trace:
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at oracle.net.ns.Packet.receive(Packet.java:300)
at oracle.net.ns.DataPacket.receive(DataPacket.java:106)
at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:315)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:260)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:185)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:102)
at oracle.jdbc.driver.T4CSocketInputStreamWrapper.readNextPacket(T4CSocketInputStreamWrapper.java:124)
at oracle.jdbc.driver.T4CSocketInputStreamWrapper.read(T4CSocketInputStreamWrapper.java:80)
at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1137)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:290)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)
at oracle.jdbc.driver.T4C7Ocommoncall.doOLOGOFF(T4C7Ocommoncall.java:61)
at oracle.jdbc.driver.T4CConnection.logoff(T4CConnection.java:543)
at oracle.jdbc.driver.PhysicalConnection.close(PhysicalConnection.java:3984)
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:642)
at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolPooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:622)
at com.mchange.v2.resourcepool.BasicResourcePoolDestroyResourceTask.run(BasicResourcePool.java:1076)
at com.mchange.v2.resourcepool.BasicResourcePool.destroyResource(BasicResourcePool.java:1101)
at com.mchange.v2.resourcepool.BasicResourcePool.destroyResource(BasicResourcePool.java:1062)
at com.mchange.v2.resourcepool.BasicResourcePool.access0(BasicResourcePool.java:44)
at com.mchange.v2.resourcepool.BasicResourcePool.run(BasicResourcePool.java:1316)
июл 03, 2017 11:44:27 AM se.jiderhamn.classloader.leak.prevention.JULLogger info
INFO: Thread 'Thread[Resource Destroyer in BasicResourcePool.close(),5,main]' of type com.mchange.v2.resourcepool.BasicResourcePool loaded by protected ClassLoader with contextClassLoader = protected ClassLoader or child no longer alive - no action needed.
看起来一切正常,但是 Tomcat 的 "find leaks" 报告了新的内存泄漏。
解决方案
问题出在 Hibernate 依赖项中的 JBoss 日志记录。我从 hibernate-core 中排除了 jboss-logging 依赖项,将 jboss-logging-3.3.0.Final.jar 复制到 Tomcat 的 "lib" 文件夹,问题就消失了.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
<exclusions>
<exclusion>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
您的应用程序有 many third party libraries that may cause ClassLoader leaks. If you want to track down the offender in your case (which could be both fun and educational!), I recommend following the instructions in this blog post of mine. To get rid of the issue, simply add my ClassLoader Leak Prevention library 个。有时在添加该库后仅通过查看日志也会发现违规者。
@Configuration
public class DataSourceConfiguration
{
@Autowired
private Environment env;
@Bean(destroyMethod = "close")
public ComboPooledDataSource dataSource()
{
String datasourcePathStartsWith = "datasource.";
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try
{
dataSource.setContextClassLoaderSource("library");
dataSource.setPrivilegeSpawnedThreads(true);
dataSource.setDriverClass("oracle.jdbc.driver.OracleDriver");
dataSource.setJdbcUrl(env.getProperty(datasourcePathStartsWith + "url"));
dataSource.setUser(env.getProperty(datasourcePathStartsWith + "user"));
dataSource.setPassword(env.getProperty(datasourcePathStartsWith + "password"));
dataSource.setMinPoolSize(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "minPoolSize")));
dataSource.setMaxPoolSize(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "maxPoolSize")));
dataSource.setCheckoutTimeout(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "checkoutTimeout")));
dataSource.setMaxIdleTime(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "maxIdleTime")));
dataSource.setMaxStatements(Integer.parseInt(env.getProperty(datasourcePathStartsWith + "maxStatements")));
}
catch (PropertyVetoException e)
{
e.printStackTrace();
}
return dataSource;
}
}
这是我的代码。它在 Tomcat 上工作 "fine" 7 - 当我多次重新部署应用程序并使用 "Find leaks" 功能时,它什么也没显示。但是如果我添加 sessionFactory,每次重新部署都会出现问题:
以下 Web 应用程序已停止(重新加载、取消部署),但它们的
类 之前运行的仍然加载在内存中,因此导致内存
泄漏(使用分析器确认):
/testPermGen
/testPermGen
/testPermGen
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource)
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
return sessionFactoryBean;
}
我试过这段代码,没有连接到数据源,没有成功。看起来问题出在应用程序停止时未关闭的 LocalSessionFactoryBean。
@Bean(destroyMethod = "destroy")
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
return sessionFactoryBean;
}
Tomcat 是 运行 以下标志:
-XX:MaxPermSize=128m
-XX:+UseConcMarkSweepGC
-XX:+CMSClassUnloadingEnabled
-XX:+CMSPermGenSweepingEnabled
是否可以解决此内存泄漏问题?
更新:
我已将整个项目(4 个文件)上传到 GitHub 以重现内存泄漏:https://github.com/anton-09/TestPermGen
Mattias Jiderhamn 的 classloader-leak-prevention 没有多大帮助,我在 Tomcat 7:
июл 03, 2017 11:44:27 AM se.jiderhamn.classloader.leak.prevention.JULLogger warn
WARNING: Waiting for Thread 'Thread[Resource Destroyer in BasicResourcePool.close(),5,main]' of type com.mchange.v2.resourcepool.BasicResourcePool loaded by protected ClassLoader with contextClassLoader = protected ClassLoader or child for 5000 ms. Thread stack trace:
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at oracle.net.ns.Packet.receive(Packet.java:300)
at oracle.net.ns.DataPacket.receive(DataPacket.java:106)
at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:315)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:260)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:185)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:102)
at oracle.jdbc.driver.T4CSocketInputStreamWrapper.readNextPacket(T4CSocketInputStreamWrapper.java:124)
at oracle.jdbc.driver.T4CSocketInputStreamWrapper.read(T4CSocketInputStreamWrapper.java:80)
at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1137)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:290)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)
at oracle.jdbc.driver.T4C7Ocommoncall.doOLOGOFF(T4C7Ocommoncall.java:61)
at oracle.jdbc.driver.T4CConnection.logoff(T4CConnection.java:543)
at oracle.jdbc.driver.PhysicalConnection.close(PhysicalConnection.java:3984)
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:642)
at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolPooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:622)
at com.mchange.v2.resourcepool.BasicResourcePoolDestroyResourceTask.run(BasicResourcePool.java:1076)
at com.mchange.v2.resourcepool.BasicResourcePool.destroyResource(BasicResourcePool.java:1101)
at com.mchange.v2.resourcepool.BasicResourcePool.destroyResource(BasicResourcePool.java:1062)
at com.mchange.v2.resourcepool.BasicResourcePool.access0(BasicResourcePool.java:44)
at com.mchange.v2.resourcepool.BasicResourcePool.run(BasicResourcePool.java:1316)
июл 03, 2017 11:44:27 AM se.jiderhamn.classloader.leak.prevention.JULLogger info
INFO: Thread 'Thread[Resource Destroyer in BasicResourcePool.close(),5,main]' of type com.mchange.v2.resourcepool.BasicResourcePool loaded by protected ClassLoader with contextClassLoader = protected ClassLoader or child no longer alive - no action needed.
看起来一切正常,但是 Tomcat 的 "find leaks" 报告了新的内存泄漏。
解决方案
问题出在 Hibernate 依赖项中的 JBoss 日志记录。我从 hibernate-core 中排除了 jboss-logging 依赖项,将 jboss-logging-3.3.0.Final.jar 复制到 Tomcat 的 "lib" 文件夹,问题就消失了.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
<exclusions>
<exclusion>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
您的应用程序有 many third party libraries that may cause ClassLoader leaks. If you want to track down the offender in your case (which could be both fun and educational!), I recommend following the instructions in this blog post of mine. To get rid of the issue, simply add my ClassLoader Leak Prevention library 个。有时在添加该库后仅通过查看日志也会发现违规者。