Memory leak while reading Bytes from MySQL table...

Terve !

i am writing a little application that read/writes image data from/to a database. when running the application,it is using more and more memory and the method causing this is the method listed below(getBytesOfView). After calling this method 470 times the Error occurs ( the application is not using this method that excessive like in the sample code below, but still i have this memory leak) .

So, if someone could tell me why i am always ending up in a OutOfMemoryError after running this little piece of code or even could tell me how to AVOID this Error ... that would be "awesome".

Thanks in advance! Robert

about the DB: i am using a MySQL database; the datatype of the column i am reading is a longblob (since i store image data in this column);

my troubleshooting-find-the-memory-leak-code:

publicbyte[] getBytesOfView(String partID, String perspective){

if(connected){

try{

Statement stmt = connection.createStatement();

ResultSet rs = stmt.executeQuery("SELECT `data` FROM `view` WHERE partID = '"+partID+"' AND perspective = '"+perspective+"'");

if(rs.next()){

return rs.getBytes("data");

}

}catch (SQLException ex){

ex.printStackTrace();

returnnull;

}

}

returnnull;

}

publicstaticvoid main(String []args){

AccessDB accessdb =new AccessMySQL();

accessdb.establishDBConnection();

byte[] Bytes;

for(int i = 0 ; i < 1000 ; i++){

Bytes = accessdb.getBytesOfView("piratesship_plank","top");

if(Bytes==null)

System.out.println(i+". run = NULL");

else

System.out.println(i+". run len="+Bytes.length);

}

}

the output:

[...]

467. run len=138070

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

468. run len=138070

469. run len=138070

470. run len=138070

471. run len=138070

472. run len=138070

Java Result: 1

[3274 byte] By [roberta] at [2007-10-2 17:48:55]
# 1

The first thing I'd do is add a finally block to close resources:

public byte[] getBytesOfView(String partID, String perspective){

if(connected){

try {

Statement stmt = connection.createStatement();

ResultSet rs = stmt.executeQuery("SELECT `data` FROM `view` WHERE partID = '"+partID+"' AND perspective = '"+perspective+"'");

if(rs.next()){

return rs.getBytes("data");

}

} catch (SQLException ex) {

ex.printStackTrace();

}

} finally {

rs.close();

stmt.close();

}

}

return null;

}

Note that I removed your return in the catch block; it's unneeded since you return null when you exit the try anyway (and because beginners will see the return and forget that the finally will always be executed).

In JDBC, you MUST close things. A lot of large/heavyweight resources get allocated for you by JDBC and if you don't close them correctly, you run out of stuff (memory, cursors, connections, etc.).

StuDerbya at 2007-7-13 19:06:59 > top of Java-index,Database Connectivity,Java Database Connectivity (JDBC)...
# 2

In addition to Stu Derby's usual impeccable advice, I'd add the following: Close those resources in a finally block in their own individual try/catch blocks. That way you can still try to close a statement even if the result set throws an exception.

I have a utility class with static helper methods, like this:

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.naming.NamingException;

import javax.sql.DataSource;

import java.io.IOException;

import java.io.InputStream;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

import java.util.Properties;

/**

* Common database utilities Insured: MD87020 Date: Apr 24, 2005 Time: 10:26:53 AM

*/

public final class DbUtils

{

/**

* Logger for the class

*/

private static final Log logger = LogFactory.getLog(DbUtils.class);

/**

* Get a database connection

* @param is for reading a Properties file with name, value pairs for driver, url, username, and password.

* @return connection to the database

* @throws IllegalArgumentException if the input stream is null

* @throws IOException if the read fails

* @throws SQLException if the connection fails

*/

public static final Connection getConnection(InputStream is)

throws IOException, SQLException

{

if (is == null)

{

throw new IllegalArgumentException("Input stream cannot be null");

}

Connection connection = null;

try

{

Properties properties = new Properties();

properties.load(is);

connection = getConnection(properties);

}

catch (SQLException e)

{

logger.error("Could not open connection", e);

throw e;

}

catch (IOException e)

{

logger.error("Could not read properties", e);

throw e;

}

return connection;

}

/**

* Create a database connection

* @param properties containing name, value pairs for driver, url, username, password

* @return connection to the database

* @throws SQLException if the connection fails

*/

public static final Connection getConnection(Properties properties)

throws SQLException

{

return getConnection(properties.getProperty("driver"),

properties.getProperty("url"),

properties.getProperty("username"),

properties.getProperty("password"));

}

/**

* Create a connection to a database

* @param driver

* @param url

* @param username

* @param password

* @return connection to the database

* @throws SQLException if the connection fails

*/

public static final Connection getConnection(final String driver,

final String url,

final String username,

final String password)

throws SQLException

{

Connection connection = null;

try

{

Class.forName(driver).newInstance();

connection = DriverManager.getConnection(url, username, password);

}

catch (Exception e)

{

String message = "Could not open connection";

logger.error(message, e);

throw new SQLException(message);

}

return connection;

}

/**

* Perform a JNDI lookup to get a database connection

* @param jndiLookupName

* @return database connection

* @throws NamingException if the context lookup fails

* @throws SQLException if the connection fails

*/

public static final Connection getConnection(final String jndiLookupName)

throws NamingException, SQLException

{

Connection connection = null;

InitialContext context = null;

try

{

context = new InitialContext();

DataSource dataSource = (DataSource) context.lookup(jndiLookupName);

connection = dataSource.getConnection();

}

catch (NamingException e)

{

logger.error("Could not obtain naming context", e);

throw e;

}

catch (Exception e)

{

String message = "Could not open connection";

logger.error(message, e);

throw new SQLException(message);

}

finally

{

close(context);

}

return connection;

}

/**

* Close a naming context

* @param namingContext to close

*/

public static final void close(Context namingContext)

{

if (namingContext != null)

{

try

{

namingContext.close();

}

catch (NamingException ignore)

{

logger.error("Could not close naming context", ignore);

}

}

}

/**

* Close a statement and result set

* @param statement

* @param rs

*/

public static final void close(Statement statement, ResultSet rs)

{

close(rs);

close(statement);

}

/**

* Close a connection and statement

* @param connection

* @param statement

*/

public static final void close(Connection connection, Statement statement)

{

close(statement);

close(connection);

}

/**

* Close a statement, connection, and result set

* @param connection

* @param statement

* @param rs

*/

public static final void close(Connection connection,

Statement statement,

ResultSet rs)

{

close(rs);

close(statement);

close(connection);

}

/**

* Close a ResultSet

* @param rs to close

*/

public static final void close(ResultSet rs)

{

if (rs != null)

{

try

{

rs.close();

}

catch (SQLException ignore)

{

logger.error("could not close result set", ignore);

}

}

}

/**

* Close a statement

* @param statement to close

*/

public static final void close(Statement statement)

{

if (statement != null)

{

try

{

statement.close();

}

catch (SQLException ignore)

{

logger.error("could not close statement", ignore);

}

}

}

/**

* Close a Connection

* @param connection to close

*/

public static final void close(Connection connection)

{

if (connection != null)

{

try

{

connection.close();

}

catch (SQLException ignore)

{

logger.error("could not close connection", ignore);

}

}

}

/**

* Convenience method for rolling back a connection

* @param connection to rollback

*/

public static void rollback(Connection connection)

{

if (connection != null)

{

try

{

connection.rollback();

}

catch (SQLException e)

{

logger.error("could not rollback connection", e);

}

}

}

}

%

duffymoa at 2007-7-13 19:06:59 > top of Java-index,Database Connectivity,Java Database Connectivity (JDBC)...
# 3
Kiitos (thanks) for the fast reply ! Unfortunately one can only give the 'dollars' to one person...so, FCFS. But thanks to both of you! Regards. Robert
roberta at 2007-7-13 19:06:59 > top of Java-index,Database Connectivity,Java Database Connectivity (JDBC)...
# 4
Trust me, neither of us do it for the "money", but thanks for the concern...
StuDerbya at 2007-7-13 19:06:59 > top of Java-index,Database Connectivity,Java Database Connectivity (JDBC)...
# 5
Stu deserved the Dukes anyway. No worries. I would have given them to him, too.%
duffymoa at 2007-7-13 19:06:59 > top of Java-index,Database Connectivity,Java Database Connectivity (JDBC)...