3

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

在 Eclipse Memory Analysis 的帮助下,每个Report对象的 Retained Heap Size 平均为 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)),对象尺寸变小:

压缩前

压缩前

压缩后

压缩后

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

这是我的问题:

  1. 是否有任何其他数据格式的大小小于字符串?

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

  3. 消费者需要反序列化和解压缩来自Disruptor. 如果我有三个消费者,那么我需要反序列化和解压缩三次。有什么办法吗?


更新:

正如@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();
}
4

1 回答 1

0

使用 ObjectOutputStream 和压缩比使用 Disruptor 昂贵得多,它违背了使用它的目的。它可能会贵 1000 倍。

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

顺便说一句,如果您需要持久性,我会使用 Chronicle(部分是因为我写了它)这不需要压缩或 byte[] 或 Strings 来存储,持久化所有消息,您的队列是无界的并且完全不在堆中。即您的 100K 对象将使用 << 1 MB 的堆。

于 2014-03-25T08:48:50.623 回答