1

OutOfMemoryErrorGlassfishで長時間実行されているバックグラウンドプロセスに遭遇しています。メモリ分析によると、エラーがスローされた時点で、ヒープの50%がcom.mysql.JDBC4ResultSet(28.1%)とcom.mysql.jdbc.StatementImpl(22.1%)のインスタンス専用であることが示されています。

私のコードは、JDBCオブジェクトがスコープを失うとガベージコレクションされることを前提に構成されていますが、これは明らかにそうではありません。私はもともとJPAを使用してこれを行いましたが、メモリ負荷が爆発的に増加したため、JDBCに戻りましたが、JDBCステートメントとResultSetへの参照で構成される大量のメモリリークが発生しています。

したがって、Glassfishがこれらのクエリをキャッシュしているかどうか、およびそれを無効にするにはどうすればよいか疑問に思っています。データオブジェクトは非常に大きく、キャッシュする必要はありません。

以下に、1つのクラスと2つのメソッド(簡潔にするために変更)で構成されるコードの構造を含めました。

@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) {}
}

そうしないと、それらは開いた状態のままになり、実際にサーバーと DB の両方でリークします。

于 2012-06-13T12:51:53.937 に答える