Subject Re: [Firebird-Java] Memory buildup
Author Claus Holst
I have changed some source-files in JayBird, to cure this bug.

Can these changes be incorporated in the official source, if i mail them to someone ? (Roman ?)

Regards,
Claus


> I have created a test-case (see after this).
>
> The main difference is the way we calculate the memory usage. You check
> the total amount of memory the jvm uses. But I believe you need to
> subtract the free memory inside the jvm.
>
> I have made a few other changes, but they are not important.
>
> My test results:
>
> Memory before selects 604552
> Memory after selects 17251568
> Memory after commit 461384
> Commit freed 16790184
>
> Regards,
> Claus
>
>
>
> --- cut --------------------------------
>
> import java.sql.*;
> import java.util.*;
>
> public class MemoryTest
> {
>
> public static void main(String[] args)
> {
> /*
> * CREATE TABLE "TEST_TABLE" ( "ID" INTEGER, "VERY_LONG_STR"
> VARCHAR(20000) );
> */
>
> Connection connection = null;
>
> try
> {
> try
> {
> Class.forName("org.firebirdsql.jdbc.FBDriver");
> } catch (ClassNotFoundException e)
> {
> System.out.println("JDBC driver not found");
> e.printStackTrace();
> return;
> }
>
> connection = DriverManager
> .getConnection(
>
> "jdbc:firebirdsql:localhost/3050:d:\\opgaver\\MemoryTest\\db\\memorytest.fdb",
> "xxxxxx", "xxxxxx");
>
>
> System.out.println("Deleting...");
> Statement s = connection.createStatement();
>
> try
> {
> s.execute("delete from test_table");
> } finally
> {
> s.close();
> }
>
> System.out.println("Inserting...");
> int recordCount = 1; // We dont need a lot of records.
> PreparedStatement ps = connection
> .prepareStatement("INSERT INTO test_table("
> + "id, very_long_str) VALUES (?, ?)");
>
> try
> {
>
> Random rnd = new Random();
> byte[] string = new byte[19000];
> rnd.nextBytes(string);
>
> for (int i = 0; i < recordCount; i++)
> {
> ps.setInt(1, i);
> ps.setString(2, new String(string));
> ps.executeUpdate();
> }
> } finally
> {
> ps.close();
> }
>
> connection.setAutoCommit(false);
>
> System.gc();
>
> long memoryBeforeSelects = Runtime.getRuntime().totalMemory() -
> Runtime.getRuntime().freeMemory();
>
>
> System.out.println("Selecting...");
> int selectRuns = 10000;
> for (int i = 0; i < selectRuns; i++)
> {
> if ((i % 1000) == 0)
> System.out.println("Select no. " + i);
>
> Statement stmt = connection.createStatement();
> try
> {
> ResultSet rs = stmt.executeQuery("SELECT * FROM test_table");
> while (rs.next())
> {
> // just loop through result set
> }
> } finally
> {
> stmt.close();
> }
> }
> System.gc();
>
> long memoryAfterSelects = Runtime.getRuntime().totalMemory() -
> Runtime.getRuntime().freeMemory();
>
>
> connection.commit();
>
> System.gc();
>
> long memoryAfterCommit = Runtime.getRuntime().totalMemory() -
> Runtime.getRuntime().freeMemory();
>
> System.out.println("Memory before selects " + memoryBeforeSelects);
> System.out.println("Memory after selects " + memoryAfterSelects);
> System.out.println("Memory after commit " + memoryAfterCommit);
> System.out.println("Commit freed " + (memoryAfterSelects -
> memoryAfterCommit));
>
>
> } catch (SQLException e1)
> {
> e1.printStackTrace();
> } finally
> {
> if (connection != null)
> try
> {
> connection.close();
> } catch (SQLException e)
> {
> e.printStackTrace();
> }
> }
> }
> }
>
> --- cut --------------------------------
>
>
> >
> > >> Sorry, I think i did not express my self clearly.
> > >>
> > >> We do use both ResultSet.close() and Statement.close(), but they do
> > >> NOT make the cleanup. I believe this is a bug in JayBird.
> > >
> > > Ok, I will try to check this.
> >
> > I have used following code to reproduce your issue:
> >
> > int recordCount = 100;
> > PreparedStatement ps =
> > connection.prepareStatement(
> > "INSERT INTO test_table(" +
> > "id, very_long_str) VALUES (?, ?)");
> >
> > try {
> >
> > Random rnd = new Random();
> > byte[] string = new byte[19000];
> > rnd.nextBytes(string);
> >
> > for(int i = 0; i < recordCount; i++) {
> > ps.setInt(1, i);
> > ps.setBytes(2, string);
> > ps.executeUpdate();
> > }
> > } finally {
> > ps.close();
> > }
> >
> > connection.setAutoCommit(false);
> >
> > long memoryBefore = Runtime.getRuntime().totalMemory();
> > int selectRuns = 10000;
> > for(int i = 0; i < selectRuns; i++) {
> > Statement stmt = connection.createStatement();
> > try {
> > ResultSet rs = stmt.executeQuery(
> > "SELECT * FROM test_table");
> > while(rs.next()) {
> > // just loop through result set
> > }
> > } finally {
> > stmt.close();
> > }
> > }
> > System.gc();
> > long memoryAfter = Runtime.getRuntime().totalMemory();
> >
> > System.out.println("Memory before " + memoryBefore +
> > ", memory after " + memoryAfter + ", difference " +
> > ((memoryAfter - memoryBefore) / 1024 /1024));
> >
> > connection.commit();
> > System.gc();
> >
> > memoryAfter = Runtime.getRuntime().totalMemory();
> >
> >
> > System.out.println("Memory before " + memoryBefore +
> > ", memory after " + memoryAfter + ", difference " +
> > ((memoryAfter - memoryBefore) / 1024 /1024));
> >
> > However the difference before the connection.commit() and after the
> > connection.commit() is almost the same.
> >
> > Since this code does not reproduce your issue, would you be so kind to
> > create a test case that shows the memory growth.
>