Java分配:从预先存在/分配的池中分配对象
在 Java 程序中,当需要分配数千个类似大小的对象时,(在我看来)最好有一个“池”(这是单个分配),其中包含可以在需要时从中提取的保留项目。这个单一的大分配不会像数千个较小的分配那样使堆碎片那么多。
显然,没有办法专门将对象引用指向内存中的地址(为其成员字段)来设置池。即使新对象引用了池中的某个区域,该对象本身仍然需要分配。在不求助于本机操作系统库的情况下,您将如何处理这样的许多分配?
In a Java program when it is necessary to allocate thousands of similar-size objects, it would be better (in my mind) to have a "pool" (which is a single allocation) with reserved items that can be pulled from when needed. This single large allocation wouldn't fragment the heap as much as thousands of smaller allocations.
Obviously, there isn't a way to specifically point an object reference to an address in memory (for its member fields) to set up a pool. Even if the new object referenced an area of the pool, the object itself would still need to be allocated. How would you handle many allocations like this without resorting to native OS libraries?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您可以尝试使用 Commons Pool 库。
也就是说,除非我能证明 JVM 没有执行我需要的操作,否则我可能会推迟优化对象创建。
You could try using the Commons Pool library.
That said, unless I had proof the JVM wasn't doing what I needed, I'd probably hold off on optimizing object creation.
别担心。除非您对正在运行的实际代码进行了大量测试和分析,并且知道这是垃圾收集的问题并且 JVM 做得不够好,否则请将时间花在其他地方。
Don't worry about it. Unless you have done a lot of testing and analysis on the actual code being run and know that it is a problem with garbage collection and that the JVM isn't doing a good enough job, spend your time elsewhere.
如果您正在构建一个应用程序,其中可预测的响应时间非常重要,那么对象池,无论它们有多小,都会给您带来好处。同样,池化也是您尝试池化的数据集有多大以及您的计算机拥有多少物理内存的一个因素。
网络上有充足的证据显示该对象无论对象多小,池化都有利于应用程序性能。
您可以执行两个级别的池化:
这通常是应用程序设计决策。
此外,在多线程应用程序中,您希望对将分配和返回池的不同线程数量保持敏感。您当然不希望您的应用程序因争用而陷入困境 - 特别是当您同时处理数千个对象时。
If you are building an application, where a predictable response time is very important, then pooling of objects, no matter how small they are will pay you dividends. Again, pooling is also a factor of how big of a data set you are trying to pool and how much physical memory your machine has.
There is ample proof on the web that shows that object pooling, no matter how small the objects are, is beneficial for application performance.
There are two levels of pooling you could do:
This is generally an application design decision.
Also, in a multi-threaded application, you would like to be sensitive about how many different threads are going to be allocating and returning to the pool. You certainly do not want your application to be bogged down by contention - especially if you are dealing with thousands of objects at the same time.
@Dave 和 Casey,您不需要任何证据来证明连续内存布局可以提高缓存效率,这是大多数需要高性能但遵循“过于理想化”的 OOP 设计轨迹的 OOP 应用程序的主要瓶颈。
人们通常认为 GC 是导致高性能 Java 应用程序性能低下的罪魁祸首,在修复它之后,就将其保留在原处,而不实际分析应用程序的内存行为。但请注意,未缓存的内存指令本质上比算术指令更昂贵(并且由于内存访问和计算间隙而变得越来越昂贵)。因此,如果您关心性能,那么您当然应该关心内存管理。
缓存感知,或更一般的面向数据的编程,是在多种应用程序中实现高性能的关键,例如游戏或移动应用程序(以降低功耗)。
这里是 DOP 上的 SO 线程。
这里是索尼研发部门的幻灯片,展示了DOP 应用于游戏机游戏的有用性(需要高性能)。
那么如何解决 Java 通常不允许分配内存块的问题呢?我的猜测是,当程序刚刚启动时,您可以假设已分配的页面中几乎没有内部碎片。如果您现在有一个分配数千或数百万个对象的循环,那么它们可能都尽可能连续。请注意,您只需确保连续的对象在同一缓存行上延伸,在许多现代系统中,该缓存行只有 64 字节。另外,如果您确实关心应用程序的(内存)性能,请查看 DOP 幻灯片。
简而言之:始终一次分配多个对象(增加分配的时间局部性),并且,如果您的 GC 具有碎片整理功能,请提前运行它,否则尝试将此类分配减少到程序的开头。
我希望这对您有所帮助
-Domi
PS:@Dave,公共池库不连续分配对象。它仅通过将分配放入引用数组、嵌入堆栈、链表或类似结构中来跟踪分配。
@Dave and Casey, you don't need any proof to show that contiguous memory layout improves Cache efficiency, which is the major bottleneck in most OOP apps that need high performance but follow a "too idealistic" OOP-design trajectory.
People often think of the GC as the culprit causing low performance in high performance Java applications and after fixing it, just leave it at that, without actually profiling memory-behavior of the application. Note though that un-cached memory instructions are inherently more expensive than arithmetic instructions (and are getting more and more expensive due to the memory access <-> computation gap). So if you care about performance, you should certainly care about memory management.
Cache-aware, or more general, data-oriented programming, is the key to achieving high performance in many kinds of applications, such as games, or mobile apps (to reduce power consumption).
Here is a SO thread on DOP.
Here is a slideshow from the Sony R&D department that shows the usefulness of DOP as applied to a playstation game (high performance required).
So how to solve the problem that Java, does not, in general allow you to allocate a chunk of memory? My guess is that when the program is just starting, you can assume that there is very little internal fragmentation in the already allocated pages. If you now have a loop that allocates thousands or millions of objects, they will probably all be as contiguous as possible. Note that you only need to make sure that consecutive objects stretch out over the same cacheline, which in many modern systems, is only 64 bytes. Also, take a look at the DOP slides, if you really care about the (memory-) performance of your application.
In short: Always allocate multiple objects at once (increase temporal locality of allocation), and, if your GC has defragmentation, run it beforehand, else try to reduce such allocations to the beginning of your program.
I hope, this is of some help,
-Domi
PS: @Dave, the commons pool library does not allocate objects contiguously. It only keeps track of the allocations by putting them into a reference array, embedded in a stack, linked list, or similar.