如何在不实际序列化对象的情况下估计 Java 中对象的序列化大小?

Posted

技术标签:

【中文标题】如何在不实际序列化对象的情况下估计 Java 中对象的序列化大小?【英文标题】:How to estimate the serialization size of objects in Java without actually serializing them? 【发布时间】:2011-03-05 09:13:20 【问题描述】:

为了增强集群中的消息传递,重要的是在运行时了解消息的大小(我应该更喜欢处理本地还是远程)。

我只能找到有关基于 java 检测估计对象内存大小的框架。我测试了classmexer,它没有接近序列化大小和sourceforge SizeOf。

在一个小型测试用例中,SizeOf 的错误率约为 10%,并且比序列化快 10 倍。 (仍然瞬态完全破坏了估计,因为例如 ArrayList 是瞬态的但被序列化为数组,因此修补 SizeOf 并不容易。但我可以忍受)

另一方面,10% 的错误速度提高 10 倍似乎不是很好。有什么想法可以做得更好吗?

更新:我还测试了 ObjectSize (http://sourceforge.net/projects/objectsize-java)。结果似乎对非继承对象很好:(

【问题讨论】:

如何交换消息?我认为您的问题没有明确说明消息是使用 Java 序列化进行序列化的 - 只是为了确认,是这样吗? 对不起,忘了说:这是java序列化。 如果你可以序列化它们,简单的回答:***.com/questions/3938122/…。如果没有,您应该尝试***.com/questions/52353/… 的所有方法来获取内存中的表示大小,并选择误差范围较小的方法。 【参考方案1】:

只是一个想法 - 您可以先将对象序列化为字节缓冲区,获取其长度并现在决定是将缓冲区内容发送到远程位置还是进行本地处理(如果它取决于消息大小)。

缺点 - 如果以后决定不使用缓冲区,您可能会浪费时间进行序列化。但是,如果你估计你会浪费估计工作,以防你需要序列化(因为在这种情况下你首先估计并在第二步中序列化)。

【讨论】:

问题中java序列化的性能测量是使用ByteArrayOutputStream进行的。我有同样的想法,但我假设每 50 条消息都需要序列化(我正在使用演员)。因此,测量消息大小的性能影响很大。【参考方案2】:

无法以良好的精度和速度估计对象的序列化大小。例如,某些对象可能是 Pi 数字的缓存,它在运行时仅给定您需要的长度即可构建自身。所以它只会序列化 'length' 属性的 4 个字节,而对象可能会使用数百兆字节的内存来存储该 Pi 编号。

我能想到的唯一解决方案是添加您自己的接口,使用方法int estimateSerializeSize()。对于实现此接口的每个对象,您都需要调用此方法以获得正确的大小。如果某些对象没有实现它 - 你将不得不使用 SizeOf。

【讨论】:

说实话:我不明白你的意思。如果您的 PiCache 使用 4 个字节进行序列化,则所有其他“数百兆内存”都是瞬态的。或者它们与对象一起序列化。我不想通过它的构造函数来估计对象的大小。你的观点是对的,内存和序列化大小不能像那样比较。我想遵循非瞬态对象图并总结对象的内存大小会接近。序列化开销 - 如此处所述 -www.javaworld.com/community/node/2915- 可以忽略以显着提升性能 好吧,也许 PiCache 的例子不是很精确。我只是想找到一些关于对象占用的内存大大超过它将序列化到的内存的示例。但是,对象通常会实现自定义序列化,这会产生 10% 的差异。与 PiCache 示例一样 - 它可能根本没有“长度”属性,在构造函数中生成缓存,将其放入某个 List,并且仅序列化 list.size()。这种自定义序列化是您想要最小化的 10% 的原因。而且没有自动的方法来预测这种序列化。【参考方案3】:

类在运行时占用的大小不一定与它在内存中的大小有任何关系。您提到的示例是瞬态字段。其他示例包括对象实现 Externalizable 并自行处理序列化。

如果一个对象实现 Externalizable 或提供readObject()/writeObject(),那么最好的办法是将对象序列化到内存缓冲区以找出大小。它不会很快,但会很准确。

如果一个对象使用默认序列化,那么您可以修改 SizeOf 以考虑瞬态字段。

序列化许多相同类型的对象后,您可以为该类型构建一个“序列化配置文件”,将序列化大小与来自 SizeOf 的运行时大小相关联。这将允许您快速估计序列化大小(使用 SizeOf),然后将其与运行时大小相关联,以获得比 SizeOf 提供的结果更准确的结果。

【讨论】:

好点。我必须记住(假设我有一个很好的估计)如果我估计 Externalizable 的一个子类,我应该退回到序列化进行测量。 我刚刚意识到,无需实现 Externalizable 就可以覆盖 readObject()/writeObject()。所以我的“后备”策略现在成为首选:)。也许我可以通过实现我自己的只收集大小的输出流来稍微提高性能。唷。【参考方案4】:

其他答案有很多优点,缺少的一点是序列化机制可能会缓存某些对象

例如,您序列化一系列对象 A、B 和 C,它们都属于同一类,每个对象中都包含两个对象 o1 和 o2。假设对象开销为 100 字节,假设对象如下所示:

Object shared = new Object();
Object shread2 = new Object();

A.o1 = new Object()
A.o2 = shared


B.o1 = shared2
B.o2 = shared


C.o1 = shared2
C.o2 = shared

为了简单起见,我们可以说通用对象需要 50 个字节来序列化,而 A 的序列化大小是 100(开销)+ 50(o1)+ 50(o2)= 200 字节。也可以对 B 和 C 进行类似的幼稚估计。但是,如果在调用重置之前所有三个都由相同的对象输出流序列化,您将在流中看到的是 A 和 o1 和 o2 的序列化,然后是 B 和 o1 的序列化 b,但是引用o2 因为它是已经序列化的同一个对象。所以假设一个对象引用需要 16 个字节,B 的大小现在是 100(开销)+50(o1)+16(o2 的引用)=166。所以序列化所需的大小现在已经改变了! 我们可以对 C 进行类似的计算,得到 132 字节的缓存两个对象,因此所有三个对象的序列化大小不同,最大和最小之间的差异约为 33%。

因此,除非您每次都在没有缓存的情况下序列化整个对象,否则很难准确估计序列化对象所需的大小。

【讨论】:

这很好。我忘了提到只有1个对象被序列化,然后流被重置(至少我希望如此,否则会是框架的问题)。你知道javas序列化是否足够聪明,可以将相等的对象序列化一次吗?例如用“new Long(10L)”替换您的共享示例?所有对象都有自己的 Long 实例(不是 ==),但它们都是 equal()。

以上是关于如何在不实际序列化对象的情况下估计 Java 中对象的序列化大小?的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Json.Net 在不忽略子属性的情况下从 documentDB 序列化/反序列化动态/通用对象?

在不使用关系数据库的情况下在 Java 中存储和检索对象的简单方法? [关闭]

如何在不创建大缓冲区的情况下将 .NET 对象的大图序列化为 SQL Server BLOB?

如何在不使用 C# 中的 T 对象的情况下将 Json 数组转换为单个 JSON 对象?

如何在不指定估计行高的情况下使用 iOS 自动调整单元格大小?

JSON.net:如何在不使用默认构造函数的情况下反序列化?