【问题标题】:java deserializing and serializing in the same processjava反序列化和序列化在同一个进程中
【发布时间】:2013-10-23 11:46:38
【问题描述】:

我在序列化和反序列化同一 JVM 中的对象列表时遇到问题。确切地说,现在我的对象拥有对 Alphabet 对象的相同引用,该对象具有以下规则:

    VMID instanceId = new VMID();  //used in readResolve to identify persitent instances

    public Alphabet (int capacity, Class entryClass) {
        this.map = new gnu.trove.TObjectIntHashMap (capacity);
        this.entries = new ArrayList (capacity);
        this.entryClass = entryClass;
        // someone could try to deserialize us into this image (e.g., by RMI).  Handle this.
        deserializedEntries.put (instanceId, this);
    }

    public VMID getInstanceId() {
        return instanceId;
    } // for debugging

    public void setInstanceId(VMID id) { this.instanceId = id; }

    // Serialization
    private static final long serialVersionUID = 1;

    private static final int CURRENT_SERIAL_VERSION = 1;

    private void writeObject (ObjectOutputStream out) throws IOException {
        out.writeInt (CURRENT_SERIAL_VERSION);
        out.writeInt (entries.size());
        for (int i = 0; i < entries.size(); i++) {
            out.writeObject (entries.get(i));
        }
        out.writeBoolean (growthStopped);
        out.writeObject (entryClass);
        out.writeObject(instanceId);
    }

    private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException {
        int version = in.readInt ();
        int size = in.readInt();
        entries = new ArrayList (size);
        map = new gnu.trove.TObjectIntHashMap (size);
        for (int i = 0; i < size; i++) {
            Object o = in.readObject();
            map.put (o, i);
            entries. add (o);
        }
        growthStopped = in.readBoolean();
        entryClass = (Class) in.readObject();
        if (version >0 ){ // instanced id added in version 1S
            instanceId = (VMID) in.readObject();
        }
    }

    private transient static HashMap deserializedEntries = new HashMap();
    /**
     * This gets called after readObject; it lets the object decide whether
     * to return itself or return a previously read in version.
     * We use a hashMap of instanceIds to determine if we have already read
     * in this object.
     * @return
     * @throws ObjectStreamException
     */

    public Object readResolve() throws ObjectStreamException {
        Object previous = deserializedEntries.get(instanceId);
        if (previous != null){
            //System.out.println(" ***Alphabet ReadResolve:Resolving to previous instance. instance id= " + instanceId);
            return previous;
        }
        if (instanceId != null){
            deserializedEntries.put(instanceId, this);
        }
        //System.out.println(" *** Alphabet ReadResolve: new instance. instance id= " + instanceId);
        return this;
    }

现在在我的对象列表反序列化之后,在某些时候字母表引用不匹配。我使用以下方法进行了检查:

for (Instance i: finalTrainingDocs){
    if (!i.getTargetAlphabet().equals(finalTraining.getTargetAlphabet())){
        System.out.println("not equals");
        System.out.println(i.getTargetAlphabet().getInstanceId() + " " + finalTraining.getTargetAlphabet().getInstanceId());
    }
    finalTraining.add(i);
    counter++;
    System.out.println("counter " + counter);
}

并得到以下结果

counter 237
counter 238
counter 239
not equals
3ce62156867eb540:6b7f0de5:141e51fcd67:-7ffa 3ce62156867eb540:6b7f0de5:141e51fcd67:-7ffa

现在查看 VMId,因为它们是相同的,所以它不应该是同一个对象,就像上面的逻辑一样?感谢您的帮助。

【问题讨论】:

  • 那些 writeObject() 和 readObject() 方法似乎没有为默认情况添加任何值。
  • 好像是这样。它来自一个名为 mallet 的开源库,所以我不确定作者的意图是什么
  • 我认为没有足够的代码来弄清楚这里发生了什么。比如Alphabet.equals是如何实现的,VMID的代码在哪里?
  • 嗨,Stephen,字母表中没有 equals 实现,所以需要 ==,vmid 是这个docs.oracle.com/javase/7/docs/api/java/rmi/dgc/VMID.html

标签: java serialization deserialization


【解决方案1】:

一种可能性是您有竞争条件;即两个线程同时更新deserializedEntries hashmap。这可能会导致您拥有两个具有相同 instanceId 值的 Alphabet 实例。

deserializedEntries 声明为volatile 不足以防止这种情况发生。 (事实上​​,您不充分的同步甚至可能导致 hashmap 的内部数据结构被破坏。)


我不相信你正在做的是一个好主意。除了这种脆弱性(需要更多的重量级同步来修复)之外,您还面临哈希映射是内存泄漏的问题。我怀疑通过接受 Alphabet 实例重复并覆盖 equals 来处理此问题,您将获得更好的性能。

【讨论】:

    【解决方案2】:

    您正在根据版本读取 instanceId

    if (version >0 ){ // instanced id added in version 1S
        instanceId = (VMID) in.readObject();
    }
    

    所以这里需要同样的条件

     if (CURRENT_SERIAL_VERSION >0 ){ 
          out.writeObject(instanceId);
    

    【讨论】:

    • 你为什么这么认为?加在哪里?解释一下。
    • 字节码,版本一直是1,所以我认为这不会导致问题...?
    猜你喜欢
    • 2021-09-15
    • 2013-01-08
    • 2011-08-17
    • 2019-03-17
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    相关资源
    最近更新 更多