RuedigerMoeller / fast-serialization

FST: fast java serialization drop in-replacement
Apache License 2.0
1.59k stars 247 forks source link

Deserializing HashMap Exception : Illegal initial capacity: -1 #223

Closed caglartolgatetik closed 6 years ago

caglartolgatetik commented 6 years ago

I serialized an object including a hashmap field using FSTMapSerializer class, but while deserializing it, I got the following exception. We know that hashmap size cannot be negative number, but somehow while reading or writing it became -1.

Caused by: java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.nustaq.serialization.FSTObjectInput.readObjectCompatibleRecursive(FSTObjectInput.java:609)
        at org.nustaq.serialization.FSTObjectInput.readObjectCompatible(FSTObjectInput.java:574)
        at org.nustaq.serialization.FSTObjectInput.instantiateAndReadNoSer(FSTObjectInput.java:559)
        at org.nustaq.serialization.FSTObjectInput.readObjectWithHeader(FSTObjectInput.java:374)
        at org.nustaq.serialization.FSTObjectInput.readObjectFields(FSTObjectInput.java:712)
        at org.nustaq.serialization.FSTObjectInput.instantiateAndReadNoSer(FSTObjectInput.java:566)
        at org.nustaq.serialization.FSTObjectInput.readObjectWithHeader(FSTObjectInput.java:374)
        at org.nustaq.serialization.FSTObjectInput.readObjectInternal(FSTObjectInput.java:331)
        at org.nustaq.serialization.FSTObjectInput.readObject(FSTObjectInput.java:311)
        at org.nustaq.serialization.FSTObjectInput.readObject(FSTObjectInput.java:245)
        ... 3 more
Caused by: java.io.IOException: java.lang.IllegalArgumentException: Illegal initial capacity: -1
        at org.nustaq.serialization.FSTObjectInput$2.defaultReadObject(FSTObjectInput.java:1075)
        at org.nustaq.serialization.FSTObjectInput$MyObjectStream.defaultReadObject(FSTObjectInput.java:1361)
RuedigerMoeller commented 6 years ago

can you provide a testcase+jdk+fst version ?. I had similar issues because of the weird constructor mechanics of java's collection (deserializer cannot call default constructor in some cases). Did you subclass HashMap ?

ckavi commented 6 years ago

Java: java version "1.8.0_73" Java(TM) SE Runtime Environment (build 1.8.0_73-b02) Java HotSpot(TM) 64-Bit Server VM (build 25.73-b02, mixed mode) Fst Version: 2.52

We are still trying to reproduce the issue. We have not subclassed the HashMap. Please see my comments below in FstMapSerializer class.

@Override
public Object instantiate(Class objectClass, FSTObjectInput in, FSTClazzInfo serializationInfo, FSTClazzInfo.FSTFieldInfo referencee, int streamPosition) throws Exception {
    Object res = null;
    int len = in.readInt(); // readInt returns -1 in our case
    if ( objectClass == HashMap.class ) { //when objectClass is HashMap I guess it never calls the default constructor
        res = new HashMap(len);
    } else
    if ( objectClass == Hashtable.class ) {
        res = new Hashtable(len);
    } else
    {
        res = objectClass.newInstance();
    }
    in.registerObject(res, streamPosition,serializationInfo, referencee);
    Map col = (Map)res;
    for ( int i = 0; i < len; i++ ) {
        Object key = in.readObjectInternal(null);
        Object val = in.readObjectInternal(null);
        col.put(key,val);
    }
    return res;
}
ckavi commented 6 years ago

This seems to be our mistake. Since HashMap is not threadsafe it's possible to get HashMap size as -1 if you try removing items concurrently.

My idea is; if HashMap size is -1, it also means that the map is empty. Would it make sense to assume the map initial size as 0 while deserializing ( or serializing ) in this case?

RuedigerMoeller commented 6 years ago

yeah there could be a catch on this, however this would just hide a multithreading error :)