在Java中序列化和压缩Object的性能成本

时间:2014-03-25 08:04:22

标签: java serialization compression disruptor-pattern

应用程序不断接收名为Report的对象,并将对象放入Disruptor以供三个不同的消费者使用。

在Eclipse Memory Analysis的帮助下,每个Report对象的保留堆大小平均为20KB。应用程序以-Xmx2048开头,表示应用程序的堆大小为2GB。

但是,对象的数量一次大约为100,000,这意味着所有对象的总大小约为2GB。

要求是所有100,000个对象都应加载到Disruptor中,以便消费者异步使用数据。但是,如果每个对象的大小都大到20KB,那就不可能了。

所以我想将对象序列化为String并压缩它:

private static byte[] toBytes(Serializable o) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(o);
    oos.close();

    return baos.toByteArray();
}

private static String compress(byte[] str) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    GZIPOutputStream gzip = new GZIPOutputStream(out);
    gzip.write(str);
    gzip.close();
    return new String(Base64Coder.encode(out.toByteArray()));
}

compress(toBytes(Report))之后,对象大小更小:

压缩之前

Before compression

压缩后

After compression

现在,对象的字符串大约是6KB。现在好多了。

这是我的问题:

  1. 是否有其他数据格式的大小小于String?

  2. 每次调用序列化和压缩都会创建ByteArrayOutputStreamObjectOutputStream等对象。我不想创建许多对象,例如ByteArrayOutputStreamObjectOutputStream,因为我需要迭代100,000次。如何设计代码以便像ByteArrayOutputStreamObjectOutputStream这样的对象只创建一次并在每次迭代中使用它?

  3. 消费者需要从Disruptor反序列化和解压缩字符串。如果我有三个消费者,那么我需要反序列化和解压缩三次。任何方式?


  4. 更新

    正如@BoristheSpider建议的那样,序列化和压缩应该在一个动作中执行:

    private static byte[] compressObj(Serializable o) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        GZIPOutputStream zos = new GZIPOutputStream(bos);
        ObjectOutputStream ous = new ObjectOutputStream(zos);
    
        ous.writeObject(o);
        zos.finish();
        bos.flush();
    
        return bos.toByteArray();
    }
    

1 个答案:

答案 0 :(得分:0)

使用ObjectOutputStream和压缩比使用Disruptor要昂贵得多,它会破坏使用它的目的。它可能要贵1000倍。

最好不要限制一次排队的对象数量。除非您的设计存在严重错误,否则只有1000个20 KB对象的队列应该足以确保所有消费者都能高效工作。

顺便说一句,如果你需要持久性,我会使用Chronicle(部分是因为我写了它)这不需要压缩或byte []或字符串存储,持久存储所有消息,你的队列是无限的,完全脱离堆。即你的100K对象将使用<< 1 MB的堆。