【问题标题】:Is there are difference between RMI and plain object serialization?RMI 和普通对象序列化有区别吗?
【发布时间】:2014-09-08 07:52:22
【问题描述】:

我觉得有点奇怪。我有两个类通过 RMI 进行通信。如果我在生产系统上使用 RMI 传输对象图,那么在目的地,一些数据会丢失。

为了更深入地分析这一点,我将对象图序列化到磁盘上,然后通过 RMI 将其返回给调用者。在调用方我也将返回的对象写入磁盘。两个文件的大小不同。

使用小助手将它们传输回对象图我认识到在将数据传输到调用方的调用方后丢失了一些对象。

老实说,我觉得这很奇怪,我不知道发生了什么。因为同样的事情正在我的开发人员机器上工作。我预计会出现 SerializationException,但没有抛出任何异常。

我迷路了 ;-(。我希望如果一个对象序列化到磁盘有效,它也适用于 RMI。

有人知道调试此行为的第一步吗?

【问题讨论】:

  • 序列化过程中会跳过不可序列化和瞬态字段,反序列化时会产生空值。这是你看到的吗?
  • 嗨,马克,感谢您的回答。这正是我假设和看到的。我觉得生产方面的课程可能与我的开发机器(另一个版本)上的课程略有不同。我假设如果发生这样的事情我会遇到一个异常:客户端的 A 类实现了 Serializable,而主机端的 A 类没有。 (主机是调用者,客户端是RMI服务器)但是没有抛出,所以我没有得到哪些类没有被完全反序列化的信息。这正常吗?
  • 如果指定了序列 UID 并且没有在版本之间更改(但在这种情况下,两个类都需要可序列化才能开始),则不会出现异常。这些类还必须足够接近,以使反序列化器不会抛出 NoSuchField 等异常。如果类包含不可序列化的字段,您将不会得到 NotSerializable 异常。您尝试序列化自身的类不得为了接收异常而实现该接口。
  • @MarkW 序列化不会“抛出 NoSuchField 之类的异常”。

标签: java object serialization deserialization rmi


【解决方案1】:

你的一个 cmets 包含一个重要线索,让我推测可能发生的事情。如果不同系统上的类确实不同,那么它们有一些不同的地方,例如序列化和反序列化会导致信息丢失,而不会报告异常或错误。

为了没有错误,双方必须具有相同的可序列化类集(按名称),如果这些类不同,它们必须声明相同的serialVersionUID 值。如果已添加或删除可序列化数据字段,这可能会导致数据被删除而不会出错。如果没有实现自定义可序列化表单(readObject 和 writeObject 方法),这很容易发生,但即使实现了这些方法并且在类的演变过程中没有采取适当的措施,它仍然可能发生。

这是一个例子。假设有一个类A,最初定义如下:

// original version
class A implements Serializable {
    private static final long serialVersionUID = 9783425L;
    String x;
    String y;
}

现在假设在下一个版本中,该类中添加了另一个字段:

// modified version
class A implements Serializable {
    private static final long serialVersionUID = 9783425L;
    String x;
    String y;
    String z;
}

现在假设一台机器有A 类的修改版本并且它已经序列化了这个类的一些实例。它现在将这个序列化数据(通过 RMI 或通过其他方式,实际上并不重要)传输到恰好具有原始版本的类 A 的另一台机器。当这台机器反序列化A的实例时,新字段z中的序列化数据将被静默丢弃!

当朝另一个方向前进时会发生相反的情况。如果原版本A的机器向新版本的机器传输序列化数据,反序列化后,新版本类的z字段将没有数据,所以z字段将保留其默认值,即 null。

这在Section 3.1 of the Serialization Specification 中有描述,其中谈到了defaultReadObject 方法:

未出现在流中的对象的任何字段都设置为其默认值。出现在流中但不在对象中的值将被丢弃。这主要发生在更高版本的类编写了早期版本中没有的附加字段时。

我不认为这是序列化数据在系统之间可能不同的唯一可能方式,但如果类不同,那么这可能就是正在发生的事情。

如何调试这个?由于您已将序列化字节写入磁盘并且它们不同,因此您可以选择编码的序列化形式并查看差异是什么。序列化数据格式不是很复杂,但是很繁琐。

另一种方法是使用javap -private 检查不同系统上的类。您需要-private,因为即使是私有字段也可以序列化。这可能会告诉您是否在不同系统上的不同版本之间添加或删除了任何字段。

【讨论】:

  • 嗨斯图尔特,感谢您的回答。这样我就找到了原因。生产系统上的私有字段被混淆了。引入不同的属性名称,而我的开发机器上没有发生混淆。在将这些对象转移到生产系统时,所有混淆的字段都被静默删除。按照您的建议查看两个序列化文件发现了这种行为。
猜你喜欢
  • 1970-01-01
  • 2014-09-06
  • 2019-02-26
  • 2022-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多