【问题标题】:Serializing an arbitrary Java object with Kryo (getting IllegalAccessError)使用 Kryo 序列化任意 Java 对象(获取 IllegalAccessError)
【发布时间】:2015-01-01 15:15:44
【问题描述】:

动机:

为了帮助远程调试 (Java),能够请求远程服务器将任意对象发送到我的本地计算机进行检查是很有用的。但是,这意味着远程服务器必须能够序列化一个在运行时事先不知道的任意 java 对象。

所以我四处打听,偶然发现了Kryo serialization library。从Kryo's documentation 开始,一个主要特性是它在序列化任意java 对象方面非常健壮。对象不必实现Serializable,不需要无参数构造函数即可反序列化,我什至不需要在序列化之前了解对象的结构。完美!

问题:

所以为了测试 Kryo,我尝试查看是否可以序列化然后反序列化 PrintWriter 对象(即任意对象):

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.*;

public class SerializationTest {

    private static final String ioFileName = "someIO.bin";

    public static void main(String[] args) {

        // Create a PrintWriter object that I will later attempt to serialize
        PrintWriter outObj = null;
        try {
            outObj = new PrintWriter("textfile.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        // Change the PrintWriter's state as a test for later to see if state is restored after serialization and deserialization
        outObj.println("Hello");   // "Hello" held in PrintWriter's buffer

        Kryo kryo = new Kryo();    // Initialize Kryo serialization
        writeObj(kryo, outObj);    // Save PrintWriter object to file with "Hello" still in its buffer

        // Read the previously saved Printwriter object (still with "Hello" in its buffer)
        PrintWriter inObj = (PrintWriter) readObj(kryo);

        inObj.close();    // commit "Hello" to disk (using deserialized object)
        outObj.close();   // commit "Hello" to disk (using original object)

        System.out.println(inObj);
    }

    public static Object readObj(Kryo kryo) {
        Object obj = null;
        try {
            Input input = new Input(new FileInputStream(ioFileName));
            obj = kryo.readClassAndObject(input);   // ERROR HERE!!
            input.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return obj;
    }

    public static void writeObj(Kryo kryo, Object obj) {
        try {
            Output output = new Output(new FileOutputStream(ioFileName));
            kryo.writeClassAndObject(output, obj);
            output.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

序列化工作正常,但在反序列化时,第 39 行对kryo.readClassAndObject(input) 的调用会给出以下IllegalAccessError

Exception in thread "main" java.lang.IllegalAccessError: tried to access class sun.nio.cs.UTF_8 from class sun.nio.cs.UTF_8ConstructorAccess
    at sun.nio.cs.UTF_8ConstructorAccess.newInstance(Unknown Source)
    at com.esotericsoftware.kryo.Kryo$DefaultInstantiatorStrategy$1.newInstance(Kryo.java:1234)
    at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1086)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.create(FieldSerializer.java:547)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:523)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:704)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:528)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:704)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:528)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:704)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:528)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:704)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:528)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:786)
    at SerializationTest.readObj(SerializationTest.java:39)
    at SerializationTest.main(SerializationTest.java:27)

我曾希望我可以序列化和反序列化PrintWriter 对象outObj,并且该对象的状态将保持不变,因此我仍然可以使用反序列化的对象来写入本应保存在缓冲区中的"Hello"

有谁知道发生了什么以及如何纠正这个错误?

【问题讨论】:

    标签: java serialization deserialization kryo


    【解决方案1】:

    我认为,您希望 kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); 避免调用构造函数。 更多信息here

    但是,如果我可能会问,您到底为什么要序列化 ​​PrintWriter?那绝对是自找麻烦。 Kryo 不是“灵丹妙药”,虽然它的默认反序列化器可以与 大多数 类一起使用,这很实用(即使这样,您也总是需要编写自定义插件的极端情况),您可以当然不要指望它能够处理你能想到的每一个奇异的东西(并且序列化由内部 jvm 特定代码支持的类,比如sun.* 绝对有资格作为奇异的东西)。

    【讨论】:

    • 谢谢@Dima - 也许我应该解释一下,我所有这些的目的是在远程服务器上运行大量代码。因此,从远程调试的角度来看,能够随意挑选服务器上的任何对象并将其发送到我的本地机器进行检查对我来说非常有用。在这方面,我确实希望能够序列化任意且可能是“异国情调”的对象。在我的示例中,我选择序列化 PrintWriter 仅仅是因为它是我想到的第一个“异国情调”对象。那你是说这种现场远程调试不行吗?
    • 为什么不直接使用调试器呢? ;) 还是 JMX?还是豆壳?看起来你正在重新发明一辆自行车。这并非不可能,但可能比您想象的要复杂得多。我会再问一次……你到底为什么要“调试”PrintWriter
    • 我尝试过 Beanshell,但唯一的问题是它仅限于 telnet 接口并且仅以打印的字符串形式返回服务器对象。如果有人通过 telnet 客户端进行调试并与远程 Beanshell 实例进行交互,这是可以的。但是,我在本地编写了对任意对象进行分析的程序。我想将服务器对象作为对象返回,结构完整,以便我可以在本地代码中分析它们。 JMX 可以做到这一点吗?至于PrintWriter,这只是一个随机选择,看看 Kryo 是否可以处理“异国情调”的对象。
    【解决方案2】:

    这个错误在 Kryo 中很常见。问题是 UTF_8 类不是公开的,因此 Kryo 失败了。添加下面的自定义序列化程序帮助我解决了它。与 Kryo 一起发布以下序列化程序是个好主意,因为很多人都在为此苦苦挣扎。

    Custom serializer for kryo for UTF-8 and other charsets

    这样你告诉 Kryo 对于所有注册的 Charset 类,调用我的自定义序列化程序。我只是在哪里发出字符串名称。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-07
      • 1970-01-01
      • 1970-01-01
      • 2015-04-17
      • 2012-08-06
      • 2014-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多