【问题标题】:kryo.readObject cause NullPointerException with ArrayListkryo.readObject 导致 NullPointerException 与 ArrayList
【发布时间】:2014-05-30 20:20:08
【问题描述】:

当我使用 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 逐个字段进行初始化。

我做错了什么?

【问题讨论】:

    标签: java kryo


    【解决方案1】:

    使用 kryo 2.24 版本,调用

    kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
    

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

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

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

    我认为这在 2.21 版之后发生了变化

    【讨论】:

      【解决方案2】:

      我遇到了类似的空指针异常,我需要从以下位置添加自定义序列化程序:https://github.com/magro/kryo-serializers

      例如:

      kryo.register( Collections.EMPTY_LIST.getClass(), new CollectionsEmptyListSerializer() );
      

      【讨论】:

        【解决方案3】:

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

        1. 使用 protobuf 默认的序列化/反序列化方法而不是 kryo 序列化方法。例如,您可以将 Spark rdd[YourProtobufObject] 转换为 rdd[ByteString],使用 pb.toByteString() 进行序列化并使用 .parseFrom(xxByteString) 进行反序列化。实际上,这种方法并不优雅,但确实有效。
        2. 将您自己的 protobuf 类注册到 kryo。详情如下。
        • 首先,将配置添加到SparkConf。例如
        conf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer")
        .set("spark.kryo.registrator","your.own.registrator.implement.MyKryoRegistrator")
        
        • 其次,创建您自己的注册器工具。你可以使用 Twitter 开源的 Chill 项目 ProtobufSerializer 依赖或 mvnrepository,直接使用 ProtobufSerializer。 maven 依赖看起来是这样的。

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

        创建您自己的名为 MyKryoRegistrator 的 kryo 注册器工具

        class MyKryoRegistrator extends KryoRegistrator {
          override def registerClasses(kryo: Kryo): Unit = {
            kryo.register(classOf[YourProtobufObject], new ProtobufSerializer())
          }
        }
        
        • 第三,再次运行你的 Spark 作业,就可以了。

        【讨论】:

          【解决方案4】:

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

          例如,测试中流行的快捷方式Arrays.asList("a", "b") 可能会出现问题。

          解决方法:new ArrayList&lt;&gt;(asList("a", "b"))

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-02-12
            • 2019-07-16
            • 2016-03-07
            • 2017-05-16
            • 1970-01-01
            • 2014-06-09
            • 2015-11-07
            • 1970-01-01
            相关资源
            最近更新 更多