从 Hazelcast 反序列化 synchronizedSet 时的 Stackoverflow

Stackoverflow when deserializing synchronizedSet from Hazelcast

考虑以下代码片段 fetch/submit a Collections.synchronizedSet to/from Hazelcast:

IMap imap = getIMapFromHazelcastClient();
Set<UUID> mySet = (Set<UUID>)imap.get("someKey"); //This may trigger a Whosebug, see below.
if ( mySet == null ){
    mySet = Collections.synchronizedSet(new HashSet<UUID>());
}else{
    mySet = Collections.synchronizedSet(mySet);
}

//Concurrently add/remove elements from mySet

//After all concurrent operations complete, store the updated Set to Hazelcast.
//mySet should contain about 1-1.5K UUIDs at this point, every time.
imap.set("someKey", mySet);

在这段代码 运行s 几次之后(不确定有多少次,或者为什么),在尝试从 Hazelcast(上面标记)获取 Set 时会触发以下操作:

java.lang.WhosebugError
at java.base/java.io.ObjectInputStream$PeekInputStream.read(ObjectInputStream.java:2914)
at java.base/java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2930)
at java.base/java.io.ObjectInputStream$BlockDataInputStream.readUnsignedShort(ObjectInputStream.java:3439)
at java.base/java.io.ObjectInputStream$BlockDataInputStream.readUTF(ObjectInputStream.java:3497)
at java.base/java.io.ObjectInputStream.readUTF(ObjectInputStream.java:1242)
at java.base/java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:800)
at java.base/java.io.ObjectInputStream.readClassDescriptor(ObjectInputStream.java:991)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2017)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1895)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2202)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:519)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:477)
at java.base/java.util.HashSet.readObject(HashSet.java:344)
at java.base/jdk.internal.reflect.GeneratedMethodAccessor564.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at java.base/java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1226)
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2401)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2235)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712)

...About 900 lines of these
at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2540)
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2434)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2235)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712)

at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2540)
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2434)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2235)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712)
at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2540)
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2434)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2235)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1712)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:519)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:477)
at com.hazelcast.internal.serialization.impl.JavaDefaultSerializers$JavaSerializer.read(JavaDefaultSerializers.java:84)
at com.hazelcast.internal.serialization.impl.JavaDefaultSerializers$JavaSerializer.read(JavaDefaultSerializers.java:77)
at com.hazelcast.internal.serialization.impl.StreamSerializerAdapter.read(StreamSerializerAdapter.java:48)
at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toObject(AbstractSerializationService.java:187)
at com.hazelcast.client.spi.ClientProxy.toObject(ClientProxy.java:102)
at com.hazelcast.client.proxy.ClientMapProxy.get(ClientMapProxy.java:318)
...

如果相关,则应用程序和 Hazelcast (v3.12.9) 运行 在同一 JVM 版本:OpenJDK 运行时环境(内部版本 15.0.1+9-18)和同一硬件上.当 Hazelcast 在 v1.8.0 JVM 上 运行ning 时观察到相同的行为。

在存储部分用简单的 HashSet 替换 synchronizedSet 后问题似乎得到解决:

imap.set("someKey", new HashSet(mySet));

显然,在 Hazelcast 中存储一个 synchronizedSet 没有任何意义,但是 Whosebug 是否合理?我错过了什么吗?

这不会为我重新创建使用 Hazelcast 4.2-BETA-1 之前的完整代码。这与您的设置有何不同?

    public static void main(String[] args) throws Exception {
        String mapName = "so66579138";
        String key = "k";

        Config config = new Config();
        config.getNetworkConfig().getJoin().getAutoDetectionConfig().setEnabled(false);
        config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);

        HazelcastInstance hazelcast = Hazelcast.newHazelcastInstance(config);
        
        IMap<String, Set<UUID>> imap = hazelcast.getMap(mapName);
        
        Set<UUID> mySet = Collections.synchronizedSet(new HashSet<UUID>());
        for (int i = 0 ; i < 1500 ; i++) {
            mySet.add(UUID.randomUUID());
        }

        // PUT
        imap.set(key, mySet);
        
        // GET
        Set<UUID> mySet2 = imap.get(key);
        System.out.println("mySet2.size() == " + mySet2.size());
        System.out.println("mySet2.getClass().getName() == " + mySet2.getClass().getName());
        
        hazelcast.shutdown();
    }