WriteObject 没有正确编写 Set?
WriteObject not properly writing a Set?
我希望我没有在 Java 中发现错误!我是 运行 JDK 7u11(主要是因为这是我的雇主允许的认可的 JVM),我注意到一个非常奇怪的问题。
也就是说,我将数据分块到 LinkedHashSet
中,并使用 ObjectOutputStream
菊花通过 GZIpOutputStream
将其写入文件(提及这一点只是为了以防万一)。
现在,当我到达程序的另一端并且 readObject()
我注意到尺寸始终显示为 68,这是第一个尺寸。底层 table 可以多于或少于 68,但 .size()
方法总是 returns 68。更麻烦的是,当我尝试手动迭代底层 Set
时,它也停在 68.
while(...) {
oos.writeInt(p_rid);
oos.writeObject(wptSet);
wptSet.clear();
// wptSet = new LinkedHashSet<>(); // **This somehow causes the heapsize to increase dramatically, but it does solve the problem**
}
而阅读时
Set<Coordinate> coordinates = (Set<Coordinate>) ois.readObject();
coordinates.size()
总是 returns 68。现在,我也可以通过 .writeInt()
大小来解决问题,但我只能遍历 68 个成员!
注意 wptSet = new LinkedHashSet<>()
行实际上解决了这个问题。这样做的主要问题是,当在 JVisualVM 中查看程序时,它会使我的堆大小猛增。
更新:
实际上,我只是找到了一个可行的解决方法来修复重新实例化 wptSet 的内存泄漏... System.gc()
在每次调用 .clear()
之后调用它实际上可以避免内存泄漏。
无论哪种方式,我都不必这样做,而且 LinkedHashSet
的发货也不应该表现出这种行为。
好的,我想我明白你的意思了。
这里有一个重现的例子...
import java.util.*;
import java.io.*;
class Example {
public static void main(String[] args) throws Exception {
Set<Object> theSet = new LinkedHashSet<>();
final int size = 3;
for(int i = 0; i < size; ++i) {
theSet.add(i);
}
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ObjectOutputStream objectsOut = new ObjectOutputStream(bytesOut);
for(int i = 0; i < size; ++i) {
objectsOut.writeObject(theSet);
theSet.remove(i); // mutate theSet for each write
}
ObjectInputStream objectsIn = new ObjectInputStream(
new ByteArrayInputStream(bytesOut.toByteArray()));
for(;;) {
try {
System.out.println(((Set<?>)objectsIn.readObject()).size());
} catch(EOFException e) {
break;
}
}
}
}
输出为
3
3
3
这里发生的事情是 ObjectOutputStream
检测到您每次都在写同一个对象。每次写入theSet
,都会向对象写入一个"shared reference",这样每次反序列化的都是同一个对象。这在 the documentation:
中有解释
Multiple references to a single object are encoded using a reference sharing mechanism so that graphs of objects can be restored to the same shape as when the original was written.
在这种情况下,您应该使用 writeUnshared(Object)
来绕过此机制,而不是 writeObject(Object)
。
我希望我没有在 Java 中发现错误!我是 运行 JDK 7u11(主要是因为这是我的雇主允许的认可的 JVM),我注意到一个非常奇怪的问题。
也就是说,我将数据分块到 LinkedHashSet
中,并使用 ObjectOutputStream
菊花通过 GZIpOutputStream
将其写入文件(提及这一点只是为了以防万一)。
现在,当我到达程序的另一端并且 readObject()
我注意到尺寸始终显示为 68,这是第一个尺寸。底层 table 可以多于或少于 68,但 .size()
方法总是 returns 68。更麻烦的是,当我尝试手动迭代底层 Set
时,它也停在 68.
while(...) {
oos.writeInt(p_rid);
oos.writeObject(wptSet);
wptSet.clear();
// wptSet = new LinkedHashSet<>(); // **This somehow causes the heapsize to increase dramatically, but it does solve the problem**
}
而阅读时
Set<Coordinate> coordinates = (Set<Coordinate>) ois.readObject();
coordinates.size()
总是 returns 68。现在,我也可以通过 .writeInt()
大小来解决问题,但我只能遍历 68 个成员!
注意 wptSet = new LinkedHashSet<>()
行实际上解决了这个问题。这样做的主要问题是,当在 JVisualVM 中查看程序时,它会使我的堆大小猛增。
更新:
实际上,我只是找到了一个可行的解决方法来修复重新实例化 wptSet 的内存泄漏... System.gc()
在每次调用 .clear()
之后调用它实际上可以避免内存泄漏。
无论哪种方式,我都不必这样做,而且 LinkedHashSet
的发货也不应该表现出这种行为。
好的,我想我明白你的意思了。
这里有一个重现的例子...
import java.util.*;
import java.io.*;
class Example {
public static void main(String[] args) throws Exception {
Set<Object> theSet = new LinkedHashSet<>();
final int size = 3;
for(int i = 0; i < size; ++i) {
theSet.add(i);
}
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ObjectOutputStream objectsOut = new ObjectOutputStream(bytesOut);
for(int i = 0; i < size; ++i) {
objectsOut.writeObject(theSet);
theSet.remove(i); // mutate theSet for each write
}
ObjectInputStream objectsIn = new ObjectInputStream(
new ByteArrayInputStream(bytesOut.toByteArray()));
for(;;) {
try {
System.out.println(((Set<?>)objectsIn.readObject()).size());
} catch(EOFException e) {
break;
}
}
}
}
输出为
3
3
3
这里发生的事情是 ObjectOutputStream
检测到您每次都在写同一个对象。每次写入theSet
,都会向对象写入一个"shared reference",这样每次反序列化的都是同一个对象。这在 the documentation:
Multiple references to a single object are encoded using a reference sharing mechanism so that graphs of objects can be restored to the same shape as when the original was written.
在这种情况下,您应该使用 writeUnshared(Object)
来绕过此机制,而不是 writeObject(Object)
。