I am running into OutOfMemoryError on a long running background process on Glassfish. Memory analysis has shown that at the time the error is thrown 50% of the heap is dedicated to instances of com.mysql.JDBC4ResultSet (28.1%) and com.mysql.jdbc.StatementImpl (22.1%).
My code is structured on the assumption that my JDBC objects will be Garbage Collected once they loose scope however this is obviously not the case. I originally did this using JPA but the memory load exploded so I reverted to JDBC but I am still getting enormous memory leaks consisting of references to JDBC Statements & ResultSets.
So I am wondering if Glassfish is caching these queries and how I can disable that, the data objects are quite large and I really don’t need them to be cached.
I have included below the structure of my code consisting of one class and two methods (modified for brievity).
@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
}
}
}
As you’ve unwrapped the connection from the entity manager and thus completely take over the control over the resources in your own hands, you should explicitly close them yourself in the
finallyblock of the very sametryblock as where they’re been acquired as per the following standard JDBC idiom.Otherwise they will remain in an opened state and indeed leak away in both the server and the DB.