1

OutOfMemoryError在 Glassfish 上运行了一个长时间运行的后台进程。内存分析表明,在抛出错误时,50% 的堆专用于com.mysql.JDBC4ResultSet(28.1%) 和com.mysql.jdbc.StatementImpl(22.1%) 的实例。

我的代码的结构是假设我的 JDBC 对象一旦失去范围就会被垃圾收集,但显然情况并非如此。我最初使用 JPA 执行此操作,但内存负载爆炸式增长,因此我恢复为 JDBC,但仍然出现大量内存泄漏,包括对 JDBC 语句和结果集的引用。

所以我想知道 Glassfish 是否正在缓存这些查询以及如何禁用它,数据对象非常大,我真的不需要缓存它们。

我在下面包含了由一个类和两个方法组成的代码结构(为简洁起见进行了修改)。

@Stateless
public class WSDFileCollector implements Collector {

    @PersistenceContext
    private EntityManager em;

    @Override
    @Asynchronous
    public void run(final CollectorEntity collector) throws SQLException {

        final Connection connection = em.unwrap(Connection.class);
        final String table = "tmp_sat_" + collector.getSite().getId();
        final String column = "filename";
        try {
            // Create temporary table
            // Scan files & folders under a given directory.
            // Load filenames into MySQL Temporary table.

            final Statement statement = connection.createStatement();
            final ResultSet results = statement.executeQuery("SELECT filename FROM temporary_table WHERE filename NOT IN (SELECT filename FROM existing_files");
            while (results.next()) {
                final File file = new File(collector.getPath(), results.getString("filename"));
                if (file.isFile()) {
                    extractAndSave(file, collector);
                }
            }
        } finally {
            // Delete temporary table
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private void extractAndSave(final File file, final CollectorEntity collector) {
        final Connection connection = em.unwrap(Connection.class);
        try {
            // Begin a transaction
            // INSERT new file into existing_files

            // Scan the file, extract data and insert into multiple database tables.

            // Commit transaction
        } catch (final SQLException | FileNotFoundException ex) {
            // Rollback transaction
        }
    }
}
4

1 回答 1

2

当您从实体管理器中解开连接并因此完全接管了对您自己手中资源的控制时,您应该在与获取它们的位置相同的块中自己明确地关闭它们遵循标准的 JDBC 习惯用法。finallytry

// Declare resources.
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;

try {
    // Acquire resources.
    connection = getConnectionSomehow();
    statement = connection.createStatement();
    resultSet = statement.executeQuery(sql);

    // ...
} finally {
    // Close resources in reversed order.
    if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
    if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
    if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
}

否则,它们将保持打开状态,并且确实会在服务器和数据库中泄漏。

于 2012-06-13T12:51:53.937 回答