休眠连接泄漏

时间:2015-11-02 16:09:16

标签: java database hibernate rds

我有一个Java 7 Web应用程序堆栈,它使用JBoss 7.1.3和ActiveMQ 5.11.1部署到集群环境中。我使用Hibernate 4.0.1将我们的Java应用程序连接到RDS实例。

我观察到我们的环境似乎正在泄漏数据库连接。当我们梳理我们的应用程序时,我们无法确定这些额外的数据库连接可能来自何处。我们的休眠连接具有相对较短的寿命,并且总是在finally {}块内关闭。

在数据库上使用查询,我可以看到一个节点在20个连接处保持不变,另一个节点(主节点)将在连接大小上慢慢增长。最终,这个数字增长了很多,我们需要重新启动该节点上的应用程序,该节点会触发对另一个节点的故障。然后该节点将开始在连接中增长,而新重新启动的音符将保持不变为20。

我是否有可能遇到Hibernate的任何已知问题?关于如何正确调试这个的任何建议?

感谢。

更新1

我们使用的连接池定义如下:

<datasource jta="true" jndi-name="java:jboss/datasources/MySqlDS" pool-name="MySqlIDS" enabled="true" use-java-context="true" use-ccm="true">
                    <connection-url>jdbc:mysql://...</connection-url>
                    <driver>com.mysql</driver>
                    <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
                    <pool>
                        <min-pool-size>10</min-pool-size>
                        <max-pool-size>150</max-pool-size>
                        <prefill>true</prefill>
                        <use-strict-min>false</use-strict-min>
                        <flush-strategy>FailingConnectionOnly</flush-strategy>
                    </pool>
                    ...
                </datasource>

更新2

每次使用休眠时,都会调用forceCloseSessionInstance方法执行以下操作。

private void forceCloseSessionInstance() {
        Session hsession = session.get();

if (hsession != null && hsession.isActive()  && !hsession.wasRolledBack()) {
            hsession.rollback();
        }

        for( ; ; ) {
            closeSessionInstance();
            if ( session.get() == null ) {
                return;
            }
        }
    }

public void closeSessionInstance() throws HibernateException {
        Integer level = transLevel.get();
        if (level == null) {
            transLevel.set(0);
            level = transLevel.get();
        }
        if (level > 0) {
            transLevel.set(level - 1);
        } else {
            Session s = session.get();
            session.set(null);
            if (s != null)
            {
                if (s.isOpen())
                {
                    s.flush();
                    s.clear();
                    s.close();
                }
            }
        }

    }

1 个答案:

答案 0 :(得分:1)

使用finally块指示在处理结束时您想要发生什么,无论try-block中发生了什么。但是如果你在finally块中加入了多个东西,那么如果早期抛出异常,你就有可能在finally块中没有完成。这是编写可能导致连接泄漏的JDBC代码的常用方法:

try {
    ...
} finally {
    resultSet.close();
    statement.close();
    connection.close();
}

如果resultSet或语句在调用close时抛出异常(例如,由于jdbc对象试图告诉数据库服务器它可以释放它为该对象分配但发生网络打嗝的资源),然后连接不会关闭。

与您发布的代码的共性是您的清理中有很多事情发生,如果其中任何一个出现任何问题,那么会话就不会被关闭。

解决方案是限制一件事抛出的异常的范围,以防止其他事情被调用,例如通过嵌套finally块:

try {
    try {
        try {
            ...
        } finally {
            try {
                resultSet.close();
            } catch (SQLException e) {
                log.info(e);
            }
        }
    } finally {
        try {
            statement.close();
        } catch (SQLException e) {
            log.info(e);
        }
    }
} finally {
    try {    
        connection.close();
    } catch (SQLException e) {
        log.info(e);
    }
}

所以如果内部最终阻塞出现任何问题,外部的那些仍会被调用。此代码还会停止在屏蔽try-block抛出的异常时关闭资源时导致的异常(因为try-block抛出的异常是具有有用信息的异常)。

(使用try-with-resources的工作方式大致相同,只是如果try块正常完成并且任何资源抛出某些东西,那么关闭时抛出的异常不会被抑制。)