JDBC PreparedStatement Memory Leak. - Mailing list pgsql-patches

From Leif Mortenson
Subject JDBC PreparedStatement Memory Leak.
Date
Msg-id 3C96DFF8.8070607@silveregg.co.jp
Whole thread Raw
Responses Re: JDBC PreparedStatement Memory Leak.
List pgsql-patches
I encountered a very large memory leak when using PreparedStatements in
a Java Application.

The memory leak only shows itself where all of the following are true:
1) The Threads which use the PreparedStatements have a long lifetime.
2) setDate() or setTimestamp is called on the PreparedStatements.
3) The PreparedStatements are being used enough to notice the leak.

In my application, my JVM was running out of memory around 330MB after
about 1 hour of
continuous opertion. I was able to find the problem by using the Java
Profiler. It showed
that around 100,000 instances of SimpleDateFormat and their subobjects
were hanging around
in memory.

It turns out that this was being caused by the way PreparedStatement was
using
ThreadLocal instances. Each one was creating a SimpleDateFormat which
via the
ThreadLocal gets set into a HashMap in the current Thread. These never
get cleaned
up until the Thread itself is GCed.

It looks like this had been discovered last summer and there were some
posts about a
patch having been committed. But the problem is still there.

Here is the patch that I made to fix this.

Cheers,
Leif

RCS file:
/projects/cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java,v
retrieving revision 1.24
diff -w -r1.24 PreparedStatement.java
42,45c42,45
< // We use ThreadLocal for SimpleDateFormat's because they are not that
< // thread safe, so each calling thread has its own object.
< private static ThreadLocal tl_df = new ThreadLocal(); // setDate()
SimpleDateFormat
< private static ThreadLocal tl_tsdf = new ThreadLocal(); //
setTimestamp() SimpleDateFormat
---
> // Store a copy of SimpleDataFormats for each instance and synchronize
them as they
> // are not very thread safe.
> private static SimpleDateFormat df; // setDate() SimpleDateFormat
> private static SimpleDateFormat tsdf; // setTimestamp() SimpleDateFormat
351c351
< SimpleDateFormat df = (SimpleDateFormat) tl_df.get();
---
> SimpleDateFormat df = this.df;
354,355c354,356
< df = new SimpleDateFormat("''yyyy-MM-dd''");
< tl_df.set(df);
---
> // It is possible that two threads could get in here, but it
> // does not cause problems.
> this.df = df = new SimpleDateFormat("''yyyy-MM-dd''");
357c358,365
< set(parameterIndex, df.format(x));
---
>
> String formatX;
> synchronized(df)
> {
> // SimpleDateFormats are not thread safe.
> formatX = df.format(x);
> }
> set(parameterIndex, formatX);
407c415
< SimpleDateFormat df = (SimpleDateFormat) tl_tsdf.get();
---
> SimpleDateFormat df = tsdf;
409a418,419
> // It is possible that two threads could get in here, but it
> // does not cause problems.
412c422
< tl_tsdf.set(df);
---
> tsdf = df;
424a435,441
> String formatX;
> synchronized(df)
> {
> // SimpleDateFormats are not thread safe.
> formatX = df.format(x);
> }
>
429c446
<
sbuf.append("'").append(df.format(x)).append('.').append(decimal).append("+00'");
---
>
sbuf.append("'").append(formatX).append('.').append(decimal).append("+00'");




pgsql-patches by date:

Previous
From: "Rod Taylor"
Date:
Subject: Re: Domain Support -- another round
Next
From: Bruce Momjian
Date:
Subject: Re: JDBC PreparedStatement Memory Leak.