Inserting large BLOBs via JDBC - OutOfMemoryError

From: holger(dot)haag(at)gmx(dot)de
To: pgsql-jdbc(at)postgresql(dot)org
Cc: hhaag(at)gmx(dot)de
Subject: Inserting large BLOBs via JDBC - OutOfMemoryError
Date: 2002-08-15 12:48:25
Message-ID: 17295.1029415705@www55.gmx.net
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-jdbc

Hi all,

I am facing a problem when writing blobs via jdbc.

Small BLOBs work, but with BLOBs of a certain size my code throws a
java.lang.OutOfMemoryError.
The file I tried has about 2-3 MB.

Environment:
- PostgreSQL 7.2 on i686-pc-linux-gnu, compiled by GCC 2.96
- Mandrake 8.2
- 192 MB RAM
- blob column created as datatype "bytea" (maybe that is the problem)
- code posted below (BlobPostgresTest.java)
- console output posted below
- java command to run program:
java -verbose:gc -Xmx128m -classpath .:pgjdbc2.jar BlobPostgresTest
- java version as follows from "java -version" command:
java version "1.4.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0_01-b03)
Java HotSpot(TM) Client VM (build 1.4.0_01-b03, mixed mode)

To me it seems that the implementation of setBinaryStream() could be
improved.
Details follow below.

let me know what you think.
holger

Console output (including GC activities):
=========================================

java -verbose:gc -Xmx128m -classpath .:pgjdbc2.jar BlobPostgresTest

BLOB/PostgreSQL Demo started
[GC 511K->230K(1984K), 0.0163708 secs]
preparing statement
setting binary stream
[GC 402K->273K(1984K), 0.0092325 secs]
[Full GC 273K->273K(1984K), 0.0771294 secs]
[GC 4351K->4058K(4908K), 0.0046329 secs]
[Full GC 4058K->3770K(4908K), 0.0831070 secs]
[GC 4922K->4922K(6860K), 0.0009556 secs]
[Full GC 4922K->4346K(6860K), 0.0890570 secs]
[GC 6650K->6650K(7820K), 0.0011110 secs]
[Full GC 6650K->5496K(7820K), 0.1420860 secs]
[GC 10104K->10104K(14480K), 0.0012827 secs]
[Full GC 10104K->7800K(14480K), 0.1236744 secs]
[GC 17016K->17016K(24084K), 0.0015421 secs]
[Full GC 17016K->12408K(24084K), 0.1688843 secs]
[GC 30840K->30840K(43224K), 0.0020800 secs]
[Full GC 30840K->21624K(43224K), 0.2547274 secs]
[GC 76920K->74616K(81500K), 0.0041685 secs]
[Full GC 74616K->49272K(81500K), 0.5688448 secs]
[GC 67704K->67704K(88332K), 0.0033407 secs]
[Full GC 67704K->58488K(88332K), 0.2558231 secs]
executing update
[GC 95352K->95352K(104844K), 0.0932741 secs]
[Full GC 95352K->40056K(104844K), 0.9644251 secs]
[GC 69245K->69245K(104844K), 0.0036631 secs]
[Full GC 69245K->69245K(104844K), 0.0814962 secs]
[Full GC 69245K->66324K(129728K), 1.1439123 secs]
Exception in thread "main" java.lang.OutOfMemoryError

as you can see the program stops when setBinaryStream() is executed.
the memory allocated is exceeding 128 MB (the whole thing fails with a max.
heap of 192MB as well).

-> my assumption is that setBinaryStream() is allocating a huge amount of
memory

looking into the PostreSQL source code, I find

org.postgresql.jdbc1.AbstractJdbc1Statement#setBinaryStream
org.postgresql.util.PGbytea#toPGString

mainly toPGString seems to be programmed inefficiently in terms of memory
consumption, e.g.

the string buffer allocation

public static String toPGString(byte[] p_buf) throws SQLException
{
if (p_buf == null)
return null;
StringBuffer l_strbuf = new StringBuffer();

should contain a senseful inital size

public static String toPGString(byte[] p_buf) throws SQLException
{
if (p_buf == null)
return null;
StringBuffer l_strbuf = new StringBuffer(p_buf.length);

or even

StringBuffer l_strbuf = new StringBuffer(p_buf.length*2);

because of special characters

to avoid a repeated re-allocation of the internal char[] array in the
stringbuffer.

BlobPostgresTest.java
=====================

import java.sql.*;
import java.io.*;

/*

drop table blobdemo;

drop sequence blobdemo_id_seq;

create table blobdemo(
id serial not null primary key,
name varchar(50),
content bytea);

*/

public final class BlobPostgresTest {
private final static String NAME = "TEST";
//private final static String FILE_NAME = "/tmp/blob/2mb.xxx";
private final static String FILE_NAME = "BlobPostgresTest.java";

public final static void main(String[] args) throws Throwable {
Connection con = null;
Statement statement = null;
PreparedStatement insertStatement = null;
ResultSet rs = null;
File file = null;
FileInputStream fis = null;
BufferedInputStream bis = null;

try {
System.out.println("BLOB/PostgreSQL Demo started");

Class.forName("org.postgresql.Driver");

con = DriverManager.getConnection
("jdbc:postgresql://localhost/template1",
"postgres", "");
con.setAutoCommit(true);

statement = con.createStatement();

// cleanup
statement.executeUpdate("delete from blobdemo");

// file
file = new File(FILE_NAME);
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);

// insert one record
System.out.println("preparing statement");
insertStatement = con.prepareStatement
("insert into blobdemo ( name, content ) values ( ?, ? )");

insertStatement.setString(1, NAME);

System.out.println("setting binary stream");
insertStatement.setBinaryStream(2, bis, (int)file.length());

System.out.println("executing update");
insertStatement.executeUpdate();

// retrieve
rs = statement.executeQuery
("select id, name, content from blobdemo");

while(rs.next()) {
System.out.println("id=" + rs.getObject(1));
System.out.println("name=" + rs.getObject(2));

byte[] bytes = rs.getBytes(3);
String content = new String(bytes);

//System.out.println(content);
System.out.println("retrieved " + bytes.length + " bytes");
}
} finally {

if(rs != null) rs.close();
if(statement != null) statement.close();
if(insertStatement != null) insertStatement.close();
if(con != null) con.close();

if(fis != null) fis.close();
if(bis != null) bis.close();
}

System.out.println("BLOB/PostgreSQL Demo complete");
}
}

--
GMX - Die Kommunikationsplattform im Internet.
http://www.gmx.net

Browse pgsql-jdbc by date

  From Date Subject
Next Message hhaag 2002-08-15 13:09:03 Inserting large BLOBs via JDBC - OutOfMemoryError
Previous Message Bruce Momjian 2002-08-15 04:13:57 Re: CVS compile error