使用Spring的LocalSessionFactoryBean时出现Permgen问题

时间:2017-06-30 04:29:20

标签: spring tomcat c3p0 sessionfactory permgen

@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 7上运行“很好” - 当我多次重新部署应用程序并使用“查找泄漏”功能时,它什么都没有显示。但是如果我添加sessionFactory,每次重新部署都会出现问题:

以下Web应用程序已停止(重新加载,取消部署),但它们已停止 以前运行的类仍然会加载到内存中,从而导致内存 泄漏(使用分析器确认): / testPermGen
/ testPermGen
/ testPermGen

    @Bean
    public LocalSessionFactoryBean sessionFactory(DataSource dataSource)
    {
        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        return sessionFactoryBean;
    }

我尝试了这段代码,没有连接到dataSource,没有成功。看起来问题是应用程序停止时未关闭的LocalSessionFactoryBean。

    @Bean(destroyMethod = "destroy")
    public LocalSessionFactoryBean sessionFactory()
    {
        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
        return sessionFactoryBean;
    }

Tomcat正在运行以下标志:

-XX:MaxPermSize=128m
-XX:+UseConcMarkSweepGC
-XX:+CMSClassUnloadingEnabled
-XX:+CMSPermGenSweepingEnabled

可以通过内存泄漏解决这个问题吗?

更新:

我在GitHub上传了整个项目(4个文件)以重现内存泄漏: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$5 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.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:622)
    at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.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.access$100(BasicResourcePool.java:44)
    at com.mchange.v2.resourcepool.BasicResourcePool$5.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$5 loaded by protected ClassLoader with contextClassLoader = protected ClassLoader or child no longer alive - no action needed.

看起来好不容易,但Tomcat的“发现漏洞”报告了新的内存泄漏。

问题在于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>

1 个答案:

答案 0 :(得分:1)

many third party libraries可能会导致ClassLoader泄漏。如果你想在你的情况下追踪犯罪者(既有趣又有教育意义!),我建议你按照this blog post of mine中的说明进行操作。要解决此问题,只需将my ClassLoader Leak Prevention library添加到您的应用程序即可。有时候,通过在添加该库后观察日志也可以揭示罪犯。