【问题标题】:Kryo serialization caused fatal java runtime error with custom objectsKryo 序列化导致自定义对象的致命 Java 运行时错误
【发布时间】:2016-10-17 09:52:33
【问题描述】:

我正在尝试序列化两个自定义类的哈希图和集合(包含更多哈希图和集合)。

Class1:NodeStorage.java

@NotNull
private final String id;

@Nullable
private String type;

@Nullable
private HashMap<String, String> properties;

Class2:RelationshipStorage.java

    @NotNull
private final String id;

@Nullable
private String type;

@Nullable
private HashMap<String, String> properties;

@NotNull
private final NodeStorage startNode;

@NotNull
private final NodeStorage endNode;

要序列化的集合:

private HashMap<NodeStorage, NodeStorage> readsSetNode;
private HashMap<NodeStorage, NodeStorage> updateSetNode;
private ArrayList<NodeStorage>            deleteSetNode;
private ArrayList<NodeStorage>            createSetNode;

private HashMap<RelationshipStorage, RelationshipStorage> readsSetRelationship;
private HashMap<RelationshipStorage, RelationshipStorage> updateSetRelationship;
private ArrayList<RelationshipStorage>                    deleteSetRelationship;
private ArrayList<RelationshipStorage>                    createSetRelationship;

我到现在为止的尝试:

 kryo.register(NodeStorage.class, 1);
    kryo.register(RelationshipStorage.class, 2);
    kryo.register(HashMap.class, mapSerializer);

    mapSerializer.setKeyClass(NodeStorage.class, kryo.getSerializer(NodeStorage.class));
    mapSerializer.setKeyClass(RelationshipStorage.class, kryo.getSerializer(RelationshipStorage.class));
    mapSerializer.setValuesCanBeNull(false);
    mapSerializer.setKeysCanBeNull(false);
    listSerializer.setElementClass(NodeStorage.class, kryo.getSerializer(NodeStorage.class));
    listSerializer.setElementClass(RelationshipStorage.class, kryo.getSerializer(RelationshipStorage.class));
    listSerializer.setElementsCanBeNull(false);


    public byte[] serialize()
{
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    Output output = new Output(stream);
    mapSerializer.write(kryo, output, readsSetNode);
    byte[] bytes = output.toBytes();
    output.close();
    return bytes;
}

我用 kryo.writeclassandobject 尝试过,但效果不佳。我明白了:

> > #
> # A fatal error has been detected by the Java Runtime Environment:
> #
> #  SIGSEGV (0xb) at pc=0x00007f92f7f6efe0, pid=4637, tid=0x00007f92f94fd700
> #
> # JRE version: OpenJDK Runtime Environment (8.0_102-b14) (build 1.8.0_102-b14)
> # Java VM: OpenJDK 64-Bit Server VM (25.102-b14 mixed mode linux-amd64 compressed oops)
> # Problematic frame:
> # V  [libjvm.so+0x787fe0]
> #
> # Core dump written

完整代码在:https://github.com/Raycoms/thesis

声明:

private Kryo kryo = new Kryo();
MapSerializer        mapSerializer  = new MapSerializer();
CollectionSerializer listSerializer = new CollectionSerializer();

【问题讨论】:

  • 您可以尝试从 100 而不是 1 作为第一个 id 开始吗?见github.com/EsotericSoftware/kryo/issues/430
  • 将其设置为 100 和 200,根本没有改变任何东西。相同的崩溃消息。
  • 可能与线程相关 - 请参阅this GitHub issue,这似乎相似。那里链接的thread-pooling readme 至少帮助了其他一名编码人员。 (通过检查your log 中的堆栈跟踪发现这一点。)
  • 目前我只写一个线程,在发送给其他人阅读之前抛出异常。
  • 添加了声明。

标签: java linux serialization fatal-error kryo


【解决方案1】:

Kryo 不是线程安全的,实际上任何 Java 程序都有多个线程在运行。例如,您的log file 表明您在崩溃时有 25 个线程在运行。即使您的 Kryo 实例是私有的,但错误在 Kryo 的内部深处,并且可能存在不受您控制的线程或 JVM 交互。

尝试下面引用的线程池方法,详细信息在Kryo readme

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.pool.*;

KryoFactory factory = new KryoFactory() {
  public Kryo create () {
    Kryo kryo = new Kryo();
    // configure kryo instance, customize settings
    return kryo;
  }
};
// Build pool with SoftReferences enabled (optional)
KryoPool pool = new KryoPool.Builder(factory).softReferences().build();

然后,在您的 serialize() 函数中:

Kryo kryo = pool.borrow();
<... the rest of your code in serialize() before the return...>
pool.release(kryo);
return bytes;

正如自述文件指出的那样,您也可以使用回调:

// or use a callback to work with kryo - no need to borrow/release,
// that's done by `run`.
String value = pool.run(new KryoCallback() {
  public String execute(Kryo kryo) {
    return kryo.readObject(input, String.class);
  }
});

OP的最终代码

从 OP 的评论中发布到这里,以便其他人可以更轻松地阅读它。 OP 最终无法使用mapSerializer,但能够使用此代码进行序列化:

private byte[] serialize() { 
    KryoPool pool = new KryoPool.Builder(factory).softReferences().build();
    Kryo kryo = pool.borrow();
    Output output = new Output(0, 1024);
    kryo.writeClassAndObject(output, readsSetNode);
    byte[] bytes = output.toBytes();
    output.close();
    pool.release(kryo);
    return bytes;
}

注意正如@MartinGrotzke 指出的那样,根据this issue,如果您使用kryo.register(class, id),请确保id&gt;=10

【讨论】:

  • 我使用 Map 序列化程序破坏了它:Kryo kryo = pool.borrow(); MapSerializer mapSerializer = new MapSerializer(); mapSerializer.setKeyClass(NodeStorage.class, kryo.getSerializer(NodeStorage.class)); mapSerializer.setKeyClass(RelationshipStorage.class, kryo.getSerializer(RelationshipStorage.class)); mapSerializer.setValuesCanBeNull(false); mapSerializer.setKeysCanBeNull(false);输出输出 = 新输出(0, 1024); mapSerializer.write(kryo, output, readsSetNode); byte[] bytes = output.toBytes();
  • 不幸的是,我根本无法使用 mapSerializer。但是,使用以下代码对其进行序列化是有效的: private byte[] serialize() { KryoPool pool = new KryoPool.Builder(factory).softReferences().build(); Kryo kryo = pool.borrow();输出输出 = 新输出(0, 1024); kryo.writeClassAndObject(输出,读取SetNode); byte[] 字节 = output.toBytes();输出.close(); pool.release(kryo);返回字节; }
  • KryoPool 当然应该只创建一次(Kryo 实例创建非常昂贵),写这个是因为这里的示例代码建议不同,用户可能会复制/粘贴。
猜你喜欢
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多