是否值得汇总byte []和char []数组,或者只是创建它更好

时间:2010-12-21 00:55:57

标签: java performance

我的代码执行了大量的输入/输出,这通常涉及创建临时数组以保存某些大小的字节或字符 - 我经常使用4096.我开始怀疑 - 没有实际测试 - 来验证它是否会更好地集中这些数组。我的代码会变成这样的

take array from pool
try {
   read from one inputStream
   write to another outputstream using array
} finally {
   return array to pool
}
  • 使用4096更快或更简单地创建一个字节,这意味着需要一些工作来在堆上分配mem,清除4096字节等。
  • 一个池似乎更简单,它可能只是从列表中检查列表并返回数组。

UPDATE 我写了一个小程序做了两件事,创建了数组并使用了apache commons pool。两者都循环了很多次(100 * 100 * 100)并创建/获取,填充数组,然后释放。我在开始时添加了一些热身jit并忽略了那些结果。每次运行都会在创建和池形式中运行十几次,在两者之间交替。

池和创建表单之间没有什么区别。但是,如果我向一个实例返回池中的apache commons池触发的回调中添加了一个clear数组,那么该池就会比创建的表单慢得多。

5 个答案:

答案 0 :(得分:6)

在演示性能问题之前,我不会实现池化。

答案 1 :(得分:4)

对象池增加了应用程序的复杂性。一般来说,你必须处理:

  • 以线程安全的方式实现获取/发布操作,
  • 确保始终释放对象,并且在发布后永远不会保留/使用
  • 确保在下次获取之前“清理”已发布的对象,并
  • 增加和缩小游泳池大小。

如果你在池实现中出错,你可能会引入潜在的错误。

值得吗?那么,答案取决于具体情况:

  • 如果内存受到严格限制,GC暂停是一个主要问题,和/或分配和初始化对象的成本很高,那么可能是肯定的。

  • 如果“清洁”和对象的成本与分配新成本的摊销成本大致相同,那么这是值得怀疑的。

要理解最后一点,您需要了解一些基本的复制GC人体工程学。特别是如果你:

  • 假设一个可连接对象的常量大小的工作集,
  • 忽略对象终结和软/弱/幻像引用,
  • 将总堆大小增加到无穷大,

然后,为Java 中的对象分配和回收内存的摊销GC成本接近归零内存的成本。

因此,如果清理对象的成本大致相当于GC开销(归零)+对象构造函数成本,那么通过池化获得的唯一成果就是减少GC标记/复制发生的次数。但是你可以通过简单地为应用程序提供更大的堆来做同样的事情。

答案 2 :(得分:2)

您可以考虑使用java.nio.Buffer类。例如,你可以这样:

class ReadWorker {
    private ByteBuffer buffer = ByteBuffer.allocate(4096);

    public void work() {
        fillBufferWithData();
        buffer.flip();
        doSomethingWithData();
        buffer.clear();
    }
}

每次调用work都会清除缓冲区,为下次调用做好准备,但不会一直分配/释放内存。 flipclear是非常快速的操作。

每个工作人员只有一个Buffer比使用相关的同步娱乐和游戏创建一个游泳池更容易。

编辑:请注意,我假设您生成了一个固定的工作池,因此您不会一直创建新的Buffer个对象。

如果您没有固定的工作池,那么您可以考虑创建一个Buffer对象池而不是原始字节数组。这取决于你如何使用它们。

答案 3 :(得分:2)

如果想要使用数组池而不是每次需要数组时都要创建,那么不是一个真正的答案,而是需要考虑的一些要点。

  • 创建数组是元素数量的一部分。
  • 获取并释放回到池的阵列有一些固定的成本。
  • 如果数组大于4096个元素,则池的开销小于创建成本。
  • 如果重要的是清除内容,
  • 创建数组比清除数组便宜。
  • 如果敏感数据可能出现在数组中,则不应该
  • 共享数组,因为无法保证返回数组的代码仍然可以保留在引用上。

答案 4 :(得分:0)

答案是肯定的。如果您必须执行大量这些输入/输出任务,则池化将提高性能并缩小内存占用量。