3
votes

I'm investigating an interesting performance issue, where there are no calls to java.sql.Clob.free() on a frequently used resource. This method on Clob was introduced in Java 6 / JDBC 4, so it might well be that this is actually a regression introduced when upgrading from JDBC 3 to JDBC 4.

Is this a known issue in the Oracle JDBC driver? Can it be said that before, Clobs somehow magically freed themselves, whereas with JDBC 4, they MUST be freed manually? Or is there a driver setting that can be used to stay compatible with JDBC 3?

Note, the same applies to Blob, of course.

2
Are you using the Oracle vendor JDBC driver? If yes, which one? oracle.com/technetwork/database/features/jdbc/…user78706
@DragosToader: ojdbc6. I'll have to double-check the exact version...Lukas Eder

2 Answers

5
votes

Our application had to be sure to call java.sql.Clob.free() explicitly on oracle.sql.CLOB (used toJdbc() to get java.sql.Clob) with Oracle 11g and 'ojdbc6.jar' (Specification-Version '4.0' and Implementation-Version '11.2.0.3.0' in MANIFEST.MF). Otherwise the application suffered from substantial memory leaks.

1
votes

I looked up the Oracle vendor JDBC drivers (ojdbc6.jar) for Oracle Database here
I found the demo.zip file here
I unzipped it and grepped the source for clob and blob.
There's a file TemporaryLobJDBC40.java
In there, there's a sample where a temp clob and blob are created, then filled with some data, then inserted into a table via a prepared statement (parameterized INSERT).
Then, the statement is executed, closed, the temp clob and blob are freed and the transaction is committed.

Then, the author loops through the rows of the table creating permanent blob/clobs and assigning them the objects returned from getClob(), getBlob() and dumps the contents to a stream.

The permanent blobs are never freed. I assume that after every iteration, as the objects go out of scope, the garbage collector frees these objects automatically.

After the last iteration, the last two Blob / Clob objects are not explicitly freed, but get implicitly cleaned up by the garbage collector (when it decides to start) their scope ended with the last iteration. (after the })

Personally, I'd do the cleanup explicitly, but to each his own. This demo shows that you can
do it either way.

Here's the code (TemporaryLobJDBC40.java):

/*
 * This sample shows how to create
 * a temporary BLOB and CLOB, write
 * some data to them and then insert
 * them into a table. This makes a 
 * permanent copy in the table. The 
 * temp lobs are still available for
 * further use if desired until the
 * transaction is committed.
 * When fetched from the table, the
 * lobs are no longer temporary.
 *
 * This version uses the new 
 * JDBC 4.0 factory methods in 
 * java.sql.Connection and the
 * free methods in java.sql.Blob and Clob
 *
 * Testing for temporary status still 
 * requires Oracle specfiic APIs in 
 * oracle.sql.BLOB and oracle.sql.CLOB.
 *
 * It needs jdk6 or later version and ojdbc6.jar
 */

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Blob;
import java.sql.Clob;


class TemporaryLobJDBC40
{
  public static void main (String args [])
    throws Exception
  {
    Connection conn = DemoConnectionFactory.getHRConnection( args );
    LobExample.createSchemaObjects( conn );
    Blob tempBlob = conn.createBlob();
    Clob tempClob = conn.createClob();

    System.out.println ("tempBlob.isTemporary()="+
                        ((oracle.sql.BLOB)tempBlob).isTemporary());
    System.out.println ("tempClob.isTemporary()="+
                        ((oracle.sql.CLOB)tempClob).isTemporary());
    LobExample.fill(tempBlob, 100L);
    LobExample.fill(tempClob, 100L);

    String insertSql = "insert into jdbc_demo_lob_table values ( ?, ?, ? )";
    PreparedStatement pstmt = conn.prepareStatement( insertSql );
    pstmt.setString( 1, "one" );
    pstmt.setBlob( 2, tempBlob );
    pstmt.setClob( 3, tempClob );
    pstmt.execute();
    pstmt.close();
    tempBlob.free();
    tempClob.free();
    conn.commit();

    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery( "select b, c from jdbc_demo_lob_table" );
    while( rs.next() )
    {
      Blob permanentBlob = rs.getBlob(1);
      Clob permanentClob = rs.getClob(2);
      System.out.println ("permanentBlob.isTemporary()="+
                          ((oracle.sql.BLOB)permanentBlob).isTemporary());
      System.out.println ("permanentClob.isTemporary()="+
                          ((oracle.sql.CLOB)permanentClob).isTemporary());
      LobExample.dump(permanentBlob);
      LobExample.dump(permanentClob);
    }
    rs.close();
    stmt.close();
    conn.close();
  }
}