在Java中实现对象的内存压缩

在Java中实现对象的内存压缩,java,memory-management,compression,Java,Memory Management,Compression,我们有这样一个用例,我们希望压缩和存储对象(在内存中),并在需要时解压缩它们 我们要压缩的数据是多种多样的,从浮点向量到字符串再到日期 有人能推荐一些好的压缩技术来做到这一点吗 我们认为压缩的易用性和解压缩的速度是最重要的因素 谢谢。如果您需要压缩任意对象,一种可能的方法是将对象序列化为字节数组,然后使用算法(GZIP使用的算法)对其进行压缩。当需要对象时,可以对其进行解压缩和反序列化。我不确定这会有多高效,但它将是完全通用的。我所知道的最好的压缩技术是ZIP。Java支持ZipStream。您

我们有这样一个用例,我们希望压缩和存储对象(在内存中),并在需要时解压缩它们

我们要压缩的数据是多种多样的,从浮点向量到字符串再到日期

有人能推荐一些好的压缩技术来做到这一点吗

我们认为压缩的易用性和解压缩的速度是最重要的因素


谢谢。

如果您需要压缩任意对象,一种可能的方法是将对象序列化为字节数组,然后使用算法(GZIP使用的算法)对其进行压缩。当需要对象时,可以对其进行解压缩和反序列化。我不确定这会有多高效,但它将是完全通用的。

我所知道的最好的压缩技术是ZIP。Java支持ZipStream。您只需将对象序列化为字节数组,然后压缩它


提示:使用ByteArrayOutputStream、DataStream、ZipOutputStream。

一个建议是使用以下流的组合:

  • /用于序列化/反序列化Java对象
  • /用于压缩/解压缩。在java.util.zip包中还可以找到其他选项
  • /用于将数据作为字节数组存储在内存中

如果要压缩
MyObject
的实例,可以让它实现
Serializable
,然后将对象流到压缩字节数组中,如下所示:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
ObjectOutputStream objectOut = new ObjectOutputStream(gzipOut);
objectOut.writeObject(myObj1);
objectOut.writeObject(myObj2);
objectOut.close();
byte[] bytes = baos.toByteArray();
然后将
字节[]
解压缩回对象:

ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
GZIPInputStream gzipIn = new GZIPInputStream(bais);
ObjectInputStream objectIn = new ObjectInputStream(gzipIn);
MyObject myObj1 = (MyObject) objectIn.readObject();
MyObject myObj2 = (MyObject) objectIn.readObject();
objectIn.close();

JDK中实现了各种压缩算法。检查
[java.util.zip](http://download.oracle.com/javase/6/docs/api/java/util/zip/package-summary.html)
用于实现的所有算法。然而,压缩所有数据可能不是一件好事。例如,只要底层类的名称在序列化数据流中,序列化的空数组可能有几十个字节长。此外,大多数压缩算法的设计都是为了消除大数据块中的冗余。在中小型Java对象上,您可能几乎没有或根本没有任何收益。

与前面的答案类似,只是我建议您使用DeflatorOutputStream和InflatorInputStream,因为它们比其他选项更简单/更快/更小。它之所以更小,是因为它只进行压缩,而替代方案添加了文件格式扩展名,如CRC检查和头

如果大小很重要,您可能希望有一个自己的简单序列化。这是因为ObjectOutputStream有很大的开销,使小对象变得更大。(对于较大的对象,尤其是压缩时,它会有所改进)


e、 一个
整数
需要81个字节,而压缩对这么少的字节没有多大帮助。这是一个非常棘手的问题:

首先,使用
ObjectOutputStream
可能不是答案。流格式包含大量与类型相关的元数据。如果要序列化小对象,即使实现自定义序列化方法,强制元数据也会使压缩算法难以“收支平衡”

使用添加了最少(或没有)类型信息的
DataOutputStream
将获得更好的结果,但使用通用压缩算法,混合数据通常不可压缩

为了更好地压缩,您可能需要查看正在压缩的数据的属性。例如:

  • Date
    对象可以表示为
    int
    值,如果您知道其精度为1天
  • int
    值序列可以进行游程编码,如果属性正确,则可以进行增量编码
  • 等等


无论您如何做,您都需要做大量的工作来获得有价值的压缩量。在我看来,更好的办法是将对象写入数据库、数据存储或文件,并使用缓存将经常使用的对象保存在内存中。

在Java中压缩searilized对象通常不是很好。。。不太好

首先,您需要了解Java对象有许多不需要的附加信息。如果你有数百万个对象,你就有数百万次的开销

举个例子,让我们看一个双链接列表。每个元素都有一个上一个指针和一个下一个指针+存储一个长值(时间戳)+用于交互类型的字节和两个用于用户ID的整数。因为我们使用指针压缩,所以我们是6字节*2+8+4*2=28字节。Java为填充添加了8个字节+12个字节。这使得每个元素有48个字节

现在我们创建了1000万个列表,每个列表包含20个元素(过去三年用户点击事件的时间序列(我们希望找到模式))

因此,我们有2亿*48字节的元素=10GB内存(ok,不太多)

好吧,除了垃圾收集杀死了我们和JDK skyrocks内部的开销之外,我们以10GB内存结束

现在让我们使用自己的内存/对象存储。我们将其存储为列数据表,其中每个对象实际上是一行。因此,我们在一个时间戳、previous、next、userIdA和userIdB集合中有2亿行

“上一个”和“下一个”现在指向行ID,变成4字节(如果超过40亿个条目,则变成5字节(不太可能))

所以我们有8+4+4+4+4=>24*200兆=4.8GB+没有GC问题

因为timestamp列以minmax的方式存储时间戳,并且我们的时间戳都在三年内,所以我们只需要5字节来存储每个时间戳。由于指针现在是相对(+和-)存储的,并且由于点击序列是及时紧密相关的,因此我们在上一个和下一个用户ID中平均只需要2字节,对于用户ID,我们使用字典,因为点击序列用于大约500k的用户,我们每个只需要3字节<