Java分配:从预先存在的/已分配的池中分配对象



在Java程序中,当需要分配数千个类似大小的对象时,(在我看来)最好有一个"池"(这是一个单一的分配),其中包含可以在需要时从中提取的保留项。这个单一的大分配不会像数千个较小的分配那样分割堆。

显然,没有一种方法可以专门将对象引用指向内存中的地址(用于其成员字段)来建立池。即使新对象引用了池的一个区域,对象本身仍然需要被分配。如果不求助于本地操作系统库,如何处理这样多的分配?

您可以尝试使用Commons Pool库。

也就是说,除非我有证据证明JVM没有做我需要的事情,否则我可能会推迟优化对象创建。

别担心。除非您对正在运行的实际代码进行了大量的测试和分析,并且知道这是垃圾收集的问题,并且JVM没有做得足够好,否则请将时间花在其他地方。

如果您正在构建一个应用程序,其中可预测的响应时间非常重要,那么将对象池化(无论它们有多小)都会给您带来好处。再一次,池化也是你试图池化的数据集有多大以及你的机器有多少物理内存的一个因素。

web上有充分的证据表明,对象池,无论对象有多小,都有利于提高应用程序的性能。

有两个级别的池:

  • 基本对象的池化,例如向量,每次您必须使用向量来形成地图或其他时从池中检索。
  • 将最常用的高级复合对象池化

这通常是一个应用程序设计决策。

同样,在多线程应用程序中,您希望对将要分配和返回池的不同线程的数量保持敏感。您当然不希望您的应用程序因争用而陷入困境—特别是当您同时处理数千个对象时。

@Dave和Casey,你们不需要任何证据来证明连续内存布局提高了缓存效率,这是大多数OOP应用程序的主要瓶颈,这些应用程序需要高性能,但遵循"过于理想主义"的OOP设计轨迹。

人们通常认为GC是导致高性能Java应用程序性能低下的罪魁祸首,在修复它之后,就别管它了,而不实际分析应用程序的内存行为。请注意,非缓存内存指令本身比算术指令更昂贵(并且由于内存访问<->计算差距而变得越来越昂贵)。所以如果你关心性能,你当然应该关心内存管理。

缓存感知,或者更一般的,面向数据的编程,是在许多类型的应用程序中实现高性能的关键,例如游戏或移动应用程序(以减少功耗)。

这是DOP上的一个SO线程。

这是索尼研发部门的幻灯片,展示了DOP应用于playstation游戏(要求高性能)的有用性。

那么如何解决Java通常不允许分配内存块的问题呢?我的猜测是,当程序刚刚启动时,您可以假设已经分配的页面中只有很少的内部碎片。如果您现在有一个分配数千或数百万个对象的循环,那么它们可能都尽可能地连续。注意,您只需要确保连续的对象在相同的cacheline上伸展,在许多现代系统中,cacheline只有64字节。另外,如果您真的关心应用程序的(内存)性能,请查看DOP幻灯片。

简而言之:总是一次分配多个对象(增加分配的时间局部性),并且,如果您的GC有碎片整理,请事先运行它,否则尝试在程序开始时减少此类分配。

我希望,这是一些帮助,受

PS: @Dave,公共池库不会连续分配对象。它只通过将它们放入引用数组、嵌入到堆栈、链表或类似的数组中来跟踪分配。

最新更新