|
Quick Lists
|
|
Bug ID:
|
4363937
|
|
Votes
|
14
|
|
Synopsis
|
ObjectOutputStream is creating a memory leak
|
|
Category
|
java:serialization
|
|
Reported Against
|
1.3
, 1.3.1
, kest-linux-fcs
|
|
Release Fixed
|
|
|
State
|
11-Closed,
Not a Defect,
bug
|
|
Priority:
|
4-Low
|
|
Related Bugs
|
4504228
,
6525563
|
|
Submit Date
|
18-AUG-2000
|
|
Description
|
java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)
When using a loop to serialize objects to a file, the ObjectOutputStream customer
never seems to release the references to objects that have already been written
to the file. Since the garbage collector can't clean up these objects, it's
relatively simple to generate an OutOfMemoryError.
Here is some sample code:
public class MyObject() {
static public void main(String[] args) {
try {
File file = File.createTempFile("test", null);
FileOutputStream testFile = new FileOutputStream
(file.getAbsolutePath(), true);
ObjectOutputStream testOut = new ObjectOutputStream(testFile);
// Write a bunch of double arrays to the file
for (int i = 0; i < 100; i++) {
testOut.writeObject(new double[1000000]);
testOut.flush();
}
testOut.close();
testFile.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
Before the program finishes, an OutOfMemoryError is generated (using the
default memory allocation for the virtual machine). Shouldn't each double
array be marked for garbage collection once it has been written to the stream?
If so, is this an io error or a garbage collection error?
(Review ID: 108636)
======================================================================
java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0)
Java HotSpot(TM) Client VM (build 1.3.0, mixed mode)
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.File;
import java.io.FileOutputStream;
public class MemoryTest
{
public static void main(String[] args)
{
try
{
File file = new File("/dev/null");
long quantity = Long.parseLong(args[0]);
ObjectOutputStream out
= new ObjectOutputStream(new FileOutputStream(file));
for(long i = 0; i < quantity; i++)
{
out.writeObject(new Long(i));
// eval1127: even a flush() here after every 200 objects did not help
if ((i % 50000) == 0)
{
System.out.println(""+i);
}
}
}
catch(Exception e)
{
System.out.println(e.toString());
}
}
}
The above code produces an OutOfMemoryError when the iteration count is high
enough.
Suggest you run as:
java -Xmx10M -Xms10M MemoryTest 10000000
This produces the following output:
0
50000
100000
150000
200000
250000
Exception in thread "main" java.lang.OutOfMemoryError
<<no stack trace available>>
This has been tested on Linux, Solaris and Windows NT. All were using JDK1.3.
All produced the same output.
I'm assuming the error is something to do with Serialization as the error does
not seem to occur when the writeObject line is removed.
(Review ID: 110753)
======================================================================
|
|
Work Around
|
None.
======================================================================
Call ObjectOutputStream.reset(). See evaluation for details.
xxxxx@xxxxx 2001-01-10
|
|
Evaluation
|
This is not a bug; it is simply how serialization works. As noted in the
comments for this bug, ObjectOutputStream must maintain a table of written
objects in order to determine when to write back-references to objects already
serialized in the stream.
The comments also note that ObjectOutputStream's handle table could use weak
references instead to avoid unnecessarily pinning objects. While attractive,
this would slow performance; moreover, the serialization protocol does not
include any means for a sender to indicate to a receiver that a given object
has been reclaimed on the sending side and hence should be released in the
receiver's handle table as well. Thus, a memory leak would still exist on the
receiving side, and using weak references on the sending side would encourage
programming practices (i.e., not calling ObjectOutputStream.reset()) which
would exacerbate the receiver's memory leak.
The proper way to prevent memory leaks when serializing large numbers of
objects is to periodically reset() the ObjectOutputStream. In addition to
clearing the sender's handle table, this sends a reset code to the receiver,
indicating that the receiving ObjectInputStream should also clear its handle
table, in the process unpinning objects on both sides of the communication.
xxxxx@xxxxx 2001-01-10
|
|
Comments
|
Submitted On 16-NOV-2000
ToBe
ObjectOutputStream includes a "feature" that enables it to
serialize objects that contain circular references.
Unfortunately, this feature is implemented in such a way
that ObjectOutputStream keeps strong references to all
objects that are written. This will in time result in a
OutOfMemoryError when a large enough amount of objects have
been written using the same ObjectOutputStream object. The
same will happen when reading a large enough amount of
objects using ObjectInputStream.
The normal way to prevent this from happening is to call
the ObjectOutputStream.reset() method regularly, to clear
all strong references. However, in JDK1.3 a bug was
introduced in that method (see bug id 4332184) that
prevented the references from ever being cleared. So, the
only way to make written objects eligible for garbage
collection is to clear all references to the
ObjectOutputStream object itself (again, see bug id 4332184
for a more complete description of this workaround). Of
course, yet another workaround is to use JDK1.2 instead of
1.3...
I consider this a SERIOUS bug, and I sincerely hope that it
will be fixed in a maintenance release in the near future
and not in Merlin (JDK1.4) as stated in the evaluation of
bug 4332184. But it didn't really put my mind at ease when
the line "Fix is not integrated properly in Merlin." popped
up in the description of the bug a while ago. Ah, well. I
guess I'll just keep on hoping...
Submitted On 16-NOV-2000
guvener
Exactly the same error occurs with
ObjectInputStream.readObject()
Submitted On 05-DEC-2000
edlund
What's the workaround for ObjectInputStream.readObject()?
The reset() method doesn't work here.
Submitted On 08-DEC-2000
ToBe
The method reset() is not actually implemented in
ObjectInputStream, for quite good reasons i migth add. So
the only way to reset an ObjectInputStream is to reset its
associated ObjectOutputStream. By doing so a TC_RESET code
will be written to the stream and when it is read by the
ObjectInputStream a reset will be performed by calling the
private method resetStream().
Submitted On 31-JAN-2001
csize
However it should be noted that the reset method for the ObjectOutputStream does not work correctly i.e. the
wireHandle2Object array is not properly reinitialized (See Bug Report 4332184 which has been confirmed), so
while calling reset is the correct method to prevent leaks the method NEEDS to work properly.
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|
|
|
 |