2013-10-23 3 views
0

동일한 JVM에서 객체 목록을 직렬화 및 비 직렬화하는 데 문제가 있습니다. 알파벳 참조가 일치 최대 나던 어떤 시점에서, 객체 내 목록의 직렬화 afte 이제동일한 프로세스에서 java가 비 직렬화 및 직렬화

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; 
    } 

: 지금 내 개체는 다음과 같은 규칙이있는 Alphabet 객체에 동일한 기준을 유지, 정확합니다. 나는 다음과 같은 사용하여 검사를했다 :

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보고가 동일하기 때문에, 그것으로, 같은 객체 안을 돌아왔다 위의 논리? 도와 줘서 고맙다. 당신은 버전에 따라 InstanceID에 읽고있는

+0

이러한 writeObject() 및 readObject() 메서드는 기본적으로 발생하는 것 이상의 값을 추가하는 것처럼 보이지 않습니다. – EJP

+0

그 것처럼 보입니다. 그것의 오픈 소스 라이브러리에서 말렛 그래서 난 거기에 저자의 의도는 확실하지 않아요 – goh

+0

나는 여기에 무슨 일이 일어나고 있는지 알아낼 수있는 충분한 코드가 있다고 생각하지 않습니다. 예를 들어, Alphabet.equals가 어떻게 구현되고, VMID의 코드는 어디에 있는가? –

답변

-1

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

그래서 같은 조건

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

왜 그렇습니까? 너 그렇게 생각해? 어디에 추가할까요? 설명. – EJP

+0

바이트 코드, 버전이 모두 1이므로 문제가 발생할 수 있다고 생각하지 않습니다 ...? – goh

0

하나의 가능성은 당신이 경쟁 조건을 가지고 여기에 적용해야합니다; 즉 두 개의 스레드가 동시에 해시 맵을 업데이트하고 있습니다. 두 개의 Alphabet 인스턴스가 같은 값인 instanceId 값을 가질 수 있습니다.

deserializedEntriesvolatile으로 선언하는 것은이를 방지하기에 충분하지 않습니다. (그리고 사실, 당신의 부적절한 동기화는 심지어 해시 맵 '내부 데이터 구조가 손상되어 발생할 수 있습니다.)


내가 무슨 일을하는 것은 좋은 생각입니다 확신 아니에요. 이 취약성 (수정하기 위해 더 많은 가중치 동기화가 필요함) 외에도 해시 맵은 메모리 누수 문제가 있습니다. Alphabet 인스턴스가 복제되고이를 처리하기 위해 equals을 무시함으로써 더 나은 성능을 얻을 것으로 판단됩니다.