kryo.readObject导致ArrayList出现NullPointerException

11

使用Kryo反序列化ArrayList对象时,我遇到了一个NullPointerException。

Caused by: java.lang.NullPointerException   
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:215)   
at java.util.ArrayList.ensureCapacity(ArrayList.java:199)   
at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:96)
at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:22)    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:679)     
at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)

我可以看到StdInstantiatorStrategy创建了一个ArrayList,但没有调用其构造函数,导致其中的一个字段未初始化从而引发了异常。

文档中提到应首先调用无参数构造函数,如果没有可用的构造函数,则应使用StdInstantiatorStrategy进行逐个字段的初始化。

我做错了什么?

4个回答

13

使用kryo 2.24版本时,调用

kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());

覆盖默认的实例化策略,如果类有无参构造函数,则使用该构造函数。正确的做法是调用:

((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy()).setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());

这里有解释:https://github.com/EsotericSoftware/kryo

我认为自2.21版本以来已经发生了变化。


1
我遇到了类似的空指针异常,需要从https://github.com/magro/kryo-serializers添加自定义序列化器。
例如:
kryo.register( Collections.EMPTY_LIST.getClass(), new CollectionsEmptyListSerializer() );

1

我遇到了同样的问题,最终解决了。在我的情况下,我在Spark作业中使用protobuf对象。 Spark kryo序列化程序无法很好地序列化/反序列化protobuf对象。我们可以使用两种方法来解决这个问题。

  1. 使用protobuf默认的序列化/反序列化方法,而不是kryo序列化方法。例如,您可以将您的Spark rdd [YourProtobufObject] 转换为rdd [ByteString],使用pb .toByteString()进行序列化,并使用.parseFrom(xxByteString) 进行反序列化。实际上,这种方法并不优雅,但它能够解决问题。
  2. 向kryo注册您自己的protobuf类。详细信息如下。
  • 首先,向SparkConf添加配置。例如
conf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer")
.set("spark.kryo.registrator","your.own.registrator.implement.MyKryoRegistrator")
  • Second, create your own registrator implement. You can use Twitter opensource Chill project ProtobufSerializer dependency or mvnrepository, and use ProtobufSerializer directly. The maven dependency looks like this.

     <dependency>
         <groupId>com.twitter</groupId>
         <artifactId>chill_2.11</artifactId>
         <version>0.9.3</version>
     </dependency>
     <dependency>
         <groupId>com.twitter</groupId>
         <artifactId>chill-protobuf</artifactId>
         <version>0.9.3</version>
         <exclusions>
             <exclusion>
                 <groupId>com.twitter</groupId>
                 <artifactId>chill-java</artifactId>
             </exclusion>
         </exclusions>
     </dependency>
    
创建自己的Kryo注册器实现,命名为MyKryoRegistrator.
class MyKryoRegistrator extends KryoRegistrator {
  override def registerClasses(kryo: Kryo): Unit = {
    kryo.register(classOf[YourProtobufObject], new ProtobufSerializer())
  }
}

第三步,再次运行您的Spark作业,它将正常运行。

0

同时要注意如何创建列表,有一些方法可以创建不可修改的列表,这可能会导致序列化问题。

例如,在测试中常用的快捷方式Arrays.asList("a", "b")可能会有问题。

解决方法:new ArrayList<>(asList("a", "b"))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接