在什么时候值得重用Java中的数组?

Bob*_*Gee 31 java optimization performance memory-management

在值得重用之前,缓冲区需要有多大的缓冲区?

或者,换一种方式:我可以重复分配,使用和丢弃byte []对象或运行池来保存和重用它们.我可能会分配很多经常被丢弃的小缓冲区,或者一些不会被丢弃的大缓冲区.汇集它们的大小比重新分配要便宜,小分配与大分配相比如何?

编辑:

好的,具体参数.说英特尔酷睿2双核CPU,最新的虚拟机版本,适用于操作系统.这个问题并不像听起来那么模糊......一些小代码和一个图表可以回答它.

EDIT2:

你发布了很多很好的一般规则和讨论,但这个问题确实要求数字.发布'em(和代码)! 理论很棒,但证据就是数字.如果各个系统的结果有所不同并不重要,我只是在寻找一个粗略的估计(数量级).似乎没有人知道性能差异是1.1,2,10或100+的因素,这是重要的.对于使用大型阵列的任何Java代码来说都很重要 - 网络,生物信息学等.

建议获得良好的基准:

  1. 在基准测试中运行之前预热代码.应该至少调用1000次10000次方法以获得完整的JIT优化.
  2. 确保基准测试方法运行至少1 10秒,并尽可能使用System.nanotime,以获得准确的计时.
  3. 在仅运行最少应用程序的系统上运行基准测试
  4. 运行基准测试3-5次并报告所有时间,因此我们看到它是多么一致.

我知道这是一个模糊且有点苛刻的问题.我会定期查看这个问题,答案会得到评论并持续评分.懒惰的答案不会(见下面的标准).如果我没有任何彻底的答案,我会附上一笔赏金.无论如何,我可能会额外奖励一个非常好的答案.

我所知道的(不需要重复):

  • Java内存分配和GC速度快,速度越来越快.
  • 对象池曾经是一个很好的优化,但现在它在大多数时候都会损害性能.
  • 对象池"通常不是一个好主意,除非创建对象很昂贵".Yadda yadda.

我不知道的是:

  • 我期望在标准的现代CPU上运行(MB/s)内存分配的速度有多快?
  • 分配大小如何影响分配率?
  • 分配数量/大小与池中重用的收支平衡点是什么?

路由到ACCEPTED答案(越多越好):

  • 最近的白皮书显示了现代CPU上的分配和GC数据(最近一年左右,JVM 1.6或更高版本)
  • 我可以运行一个简洁而正确的微基准的代码
  • 解释分配如何以及为何影响性能
  • 真实世界的例子/轶事来自于测试这种优化

上下文:

我正在开发一个库,为Java添加LZF压缩支持.该库通过添加额外的压缩级别(更多压缩)以及与来自C LZF库的字节流的兼容性来扩展H2 DBMS LZF类.我正在考虑的一件事是,是否值得尝试重用用于压缩/解压缩流的固定大小的缓冲区.缓冲器可以是~8kB,或~32kB,并且在原始版本中它们是~128kB.可以为每个流分配缓冲器一次或多次.我正在试图找出我想如何处理缓冲区以获得最佳性能,并着眼于将来可能的多线程.

是的,如果有人有兴趣使用它,库将作为开源发布.

Ste*_*n C 26

如果你想要一个简单的答案,那就是没有简单的答案.没有任何呼叫答案(并暗示人们)"懒惰"会有所帮助.

我期望在标准的现代CPU上运行(MB/s)内存分配的速度有多快?

以JVM可以使内存为零的速度,假设分配不会触发垃圾回收.如果它确实触发了垃圾收集,则无法在不知道使用什么GC算法,堆大小和其他参数的情况下进行预测,并且在应用程序的生命周期内分析应用程序的非垃圾对象的工作集.

分配大小如何影响分配率?

往上看.

分配数量/大小与池中重用的收支平衡点是什么?

如果你想要一个简单的答案,那就是没有简单的答案.

黄金法则是,堆越大(可用的物理内存量),GC垃圾对象的摊销成本越小.使用快速复制垃圾收集器,随着堆变大,释放垃圾对象的摊销成本接近零.GC的成本实际上由(简单来说)GC必须处理的非垃圾对象的数量和大小决定.

在您的堆很大的假设下,分配和GC大型对象(在一个GC循环中)的生命周期成本接近分配对象时将内存归零的成本.

编辑:如果你想要的只是一些简单的数字,写一个简单的应用程序,分配和丢弃大缓冲区并在你的机器上使用各种GC和堆参数运行它,看看会发生什么.但要注意,这不会给你一个现实的答案,因为真正的GC成本取决于应用程序的非垃圾对象.

我不会为你写一个基准,因为我知道它会给你一些假的答案.

编辑2:回应OP的评论.

所以,我应该期望分配的运行速度与System.arraycopy一样快,或者完全JITed数组初始化循环(在我的最后一个工作台上大约1GB/s,但我怀疑结果)?

理论上是的.实际上,很难以将分配成本与GC成本分开的方式进行衡量.

根据堆大小,您是说为JVM使用分配更大量的内存实际上会降低性能吗?

不,我说它可能会提高性能.显著.(前提是您没有遇到OS级别的虚拟内存效果.)

分配只适用于数组,我的代码中的其他几乎所有内容都在堆栈上运行.它应简化测量和预测性能.

也许.坦率地说,我认为你不会通过回收缓冲来获得很大的改善.

但是如果您打算沿着这条路走下去,请创建一个包含两个实现的缓冲池接口.第一个是真正的线程安全缓冲池,可以循环缓冲区.第二个是虚拟池,每次alloc调用时都会简单地分配一个新的缓冲区,并将其视为dispose无操作.最后,允许应用程序开发人员通过setBufferPool方法和/或构造函数参数和/或运行时配置属性在池实现之间进行选择.应用程序还应该能够提供自己制作的缓冲池类/实例.


aku*_*uhn 13

当它比年轻​​的空间大.

如果您的数组大于线程本地年轻空间,则直接在旧空间中分配.旧空间上的垃圾收集比年轻空间慢.因此,如果您的数组大于年轻空间,则重用它可能是有意义的.

在我的机器上,32kb超过了年轻的空间.因此重用它是有意义的.