Subject Re: [Firebird-Java] Memory buildup
Author Claus Holst
Hi Roman

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.