FULL PRODUCT VERSION :
Every one since 1.4 - including 1.6
ADDITIONAL OS VERSION INFORMATION :
All of them!
A DESCRIPTION OF THE PROBLEM :
When using ByteBuffers et al, there needs to be a way to release the underlying buffer rather than wait for garbage collection to kick in.
Such buffers may, by their very definition (and the JavaDocs own usage recommendations) be very large. These behemoths are 'fronted' by relatively tiny Java classes. The GC may not kick in in any meaningful time-frame. There are already close() (or similar) methods on classes like FileReader, since it is standard and good practice to release operating system resources as soon as possible.
These memory blocks should be considered the same - significant and limited resources.
If you don't want to change the API because it will break existing applications, the easy solution is to create a subclass of Buffer called "FreeableBuffer" or similar with a free() method (or similar) and then make ByteBuffer et al a subclass of FreeableBuffer.
Here's a sample program and output from two different versions of the JDK. This was tested on Solaris 8 in 32-bit mode using JDK 1.4.-b21 and JDK 1.5.0-b64
The program is slightly contrived, but the crux is the previous ByteBuffer(s) have (in effect) been released but in the case of 1.4 GC never kicks in and it barfs.
In the case of 1.5, it's better, GC kicks in, but according to the output
of top (not included) the JVM grew to ~1281M before GC kicked in.
If I could do a bb.release() - or similar - then those resources i.e. the
big memory buffer could be free()-ed, cached and re-used or whatever.
# java -Xmx64m -verbose:gc BB
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
# N.B. I had to specify a larger heap size for this test otherwise it ran out of memory immediately.
# java -Xmx1536m -verbose:gc BB
[GC 147K->144K(4992K), 0.0149122 secs]
[Full GC 144K->97K(4992K), 0.0202993 secs]
[GC 158K->129K(4992K), 0.0024465 secs]
[Full GC 129K->97K(4992K), 0.0260971 secs]
[GC 159K->161K(4992K), 0.0008160 secs]
[Full GC 161K->98K(4992K), 0.0166681 secs]
[GC 159K->130K(4992K), 0.0011929 secs]
[Full GC 130K->97K(4992K), 0.0278316 secs]
[GC 158K->161K(4992K), 0.0020273 secs]
[Full GC 161K->97K(4992K), 0.0181044 secs]
[GC 159K->145K(4992K), 0.0041246 secs]
[Full GC 145K->98K(4992K), 0.0210584 secs]
[GC 159K->146K(4992K), 0.0058417 secs]
[Full GC 146K->98K(4992K), 0.0204990 secs]
---------- BEGIN SOURCE ----------
public class BB
public static void main(String args)
// May make these configurable using -Dxxx
final int MAX_BUFFS = 1000;
final int BUFSIZE = 300 * 1024 * 1024;
final int PUT = 1000 * 1000;
final int REPORT = 1;
final boolean store = false;
for(int i = 0; i < MAX_BUFFS; i++)
ByteBuffer bb = ByteBuffer.allocateDirect(BUFSIZE);
if (i % REPORT == 0) System.err.println("Done " + (i + 1));
---------- END SOURCE ----------
This bug can be reproduced always.
###@###.### 2005-07-13 12:21:24 GMT