如果我有包含许多重复字符串的大对象图,那么在序列化它们之前对字符串进行实习()是否有好处?这会减少传输的数据量吗?字符串会在接收端共享指针吗?
我的猜测是字符串会在发送之前被删除,从而减少了数据的大小,并且它们都将由接收端的同一个对象表示,但它们实际上并不会被强制插入接收端。 (意味着每个序列化'事务'上会创建一个新的字符串实例)
答案 0 :(得分:6)
测试很容易:
import java.io.*;
class Foo implements Serializable {
private String x;
private String y;
public Foo(String x, String y) {
this.x = x;
this.y = y;
}
}
public class Test {
public static void main(String[] args) throws IOException {
String x = new StringBuilder("hello").append(" world").toString();
String y = "hello world";
showSerializedSize(new Foo(x, y));
showSerializedSize(new Foo(x, x));
}
private static void showSerializedSize(Foo foo) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(foo);
oos.close();
System.out.println(baos.size());
}
}
我的机器上的结果:
86
77
所以看起来重复不会自动发生。
我不会使用String.intern()
本身,因为您可能不希望所有这些字符串都在正常的实习池中 - 但您总是可以使用HashSet<String>
来创建“临时”实习生池。
答案 1 :(得分:4)
ObjectOutputStream跟踪对象图(直到重置),一个对象只写一次,即使它是通过多个引用到达的。通过实习减少对象肯定会减少字节数。
在接收端,重新创建相同的对象图,因此发送端的一个字符串实例成为接收端的一个字符串实例。
答案 2 :(得分:2)
您可以使用此ObjectOutputStream
增强功能实现String
的重复数据删除。输出应该与原始版本兼容(未测试),因此不需要特殊的ObjectInputStream
。
请注意,不使用String.intern()
,而是使用私有和临时内部Map
,因此您的PermGenSpace不会被充斥。
public class StringPooledObjectOutputStream extends ObjectOutputStream {
private Map<String, String> stringPool = new HashMap<String, String>();
public StringPooledObjectOutputStream(OutputStream out) throws IOException {
super(out);
enableReplaceObject(true);
}
@Override
protected Object replaceObject(Object obj) throws IOException {
if( !(obj instanceof String) )
return super.replaceObject(obj);
String str = (String)obj;
String replacedStr = stringPool.get(str);
if( replacedStr == null ){
replacedStr = (String)super.replaceObject(str);
stringPool.put(replacedStr, replacedStr);
}
return replacedStr;
}
}
答案 3 :(得分:0)
在序列化之前,实际上没有任何好处来实现字符串。至少这不会改变序列化的任何内容。它可能有助于减少应用程序的内存。
在readUTF()
的最低级别ObjectOutPutStream
或其等效的接收方将被调用,它将为每个呼叫分配新字符串。如果您的类是可外部化的,您可以执行readUTF().intern()
以节省接收方的内存。我自己使用过这种方法,客户端应用程序的内存使用量减少了50%以上。
但请注意,如果有许多唯一字符串,那么intern()
可能会因为使用PermGen而导致内存不足问题。看到:
http://www.onkarjoshi.com/blog/213/6-things-to-remember-about-saving-memory-with-the-string-intern-method/
我只实习小于10个字符的字符串并且没有遇到任何问题。