对象池是否已被弃用?


62

我对对象池的概念非常熟悉,并且我总是尝试尽可能多地使用它。

另外,我一直认为对象池是标准规范,因为我已经观察到Java本身以及其他框架尽可能多地使用池。

最近,尽管我读了一些对我来说是全新的东西(并且违反直觉?)。

该池实际上使程序性能变差,尤其是在并发应用程序中,并且建议实例化new对象,因为在较新的JVM中,对象的实例化确实非常快。

我在书中读过: Java Concurrency in Practice

现在,我开始考虑我是否对这里的事情有所误解,因为本书的第一部分建议使用Executors重用Thread而不是创建新实例。

那么,如今对象池是否已被弃用?

Answers:


72

不推荐使用它作为一种通用技术,因为-正如您所注意到的-在现代JVM中,创建和销毁短暂存在的对象本身(即内存分配和GC)非常便宜。因此,与普通的new。* 对象相比,使用手写的对象池可能更慢,更复杂且更容易出错。

但是,它仍然可以用于创建成本相对较高的特殊对象,例如DB /网络连接,线程等。

* 一旦我不得不提高爬网Java应用程序的性能。调查发现了使用对象池分配数百万个对象的企图...并且编写该对象池的聪明人使用单个全局锁使其线程安全。用普通替换池new使应用程序速度提高了30倍。


1
那么,如何确定对象的实例化是否过于昂贵?
2011年

3
如果对象消耗了操作系统资源(线程,I / O,共享内存等)
kevin cline

13
@ user10326,通过测量:-)如果创建对象需要很长时间,并且/或者它们与某些特殊的,可能受限制的非内存资源相关联,则可以考虑进行池化。
彼得Török

8
@ user10326,在超过95%的情况下都是IMO,通过上述条件,可以轻松轻松地预先确定是否需要对象池。(此外,在几乎所有需要使用池的情况下,您很可能会使用现有的库/框架,该库/框架可能已经为您实现了对象池。)对于其他情况,仍然很容易将对象创建隐藏在例如工厂,以后可以按照您认为合适的方式重新实现。
彼得Török

2
@Peter Torok提出的非常重要的一点:许多框架和库都为您实现了池化,始终确保在实现自己的库之前您尚未使用池化的库。
2011年

36

具体问题的答案是:“对象池是否已被弃用?” 是:

否。对象池在特定位置广泛使用-线程池,数据库连接池等。

常规对象创建从来都不是一个缓慢的过程。池本身消耗资源-内存和处理能力。任何优化都是一个权衡。

规则是:

过早的优化是邪恶的!!!

但是给定的优化何时过早?

过早的优化是在通过彻底的概要分析发现瓶颈之前进行的所有优化


2
确实。OP表示“我一直尝试尽可能多地使用它”-IMO,这就是问题所在。
nerdytenor

@Boris,所以根据您的第二句话,在通过概要分析将它们发现为瓶颈之前,我们不应该对象池数据库连接和线程吗?
Pacerier 2014年

1
@Pac一些分析结果不需要不断地重新测量:-)
David Bullock

9

在您要完全避免垃圾收集的情况下,我认为对象池是唯一可行的选择。因此,绝对不是不推荐使用的技术。


1
而且我想补充一点,每当对象的寿命足够长以至于移入较早的一代时,最好避免使用GC。
Zan Lynx

8

测量

这完全取决于您的用例,对象的大小,JVM,JVM选项,启用的GC以及其他许多因素。

简而言之:先测量,再测量。假设您正在使用对象池框架(例如来自Apache的对象池),那么在实现之间进行交换应该不会太麻烦。

额外的性能测试技巧-让JVM先进行预热,在运行的JVM上运行测试多次,它的行为可能会有所不同。


3
“首先让JVM暖机”,我记得当时唯一需要“热身”的是监视器。是的,所有新事物都变旧了。
kylben 2011年

我唯一需要热身的就是咖啡!
幻灭了

@Marijn,您如何让它“热身”?
Pacerier 2014年

请参阅JMH框架以获取完整说明(openjdk.java.net/projects/code-tools/jmh),但是基本上,您必须给JVM一个机会来JIT您的代码,在基准测试之前运行GC等等。
马汀·维尔堡

8

该池实际上使程序性能变差,尤其是在并发应用程序中,并且建议实例化新对象,因为在较新的JVM中,对象的实例化确实非常快。

取决于上下文。


1
出色的答案,而且很有说服力。我要添加(也许,也许是星号?)“ 24字节”声明涉及4个4字节浮点数(16字节)的实例,再加上4个字节的对象引用,再加上4个字节的锁引用。这是您设计消除的确切开销。
strickli

5

我不知道这里是否有变化的趋势,但是肯定会取决于它的情况。如果您的Java类正在管理外部资源(例如RMI连接或加载资源文件等),则对象实例化的成本肯定仍然很高(尽管这些资源可能已经为您汇集了!)。作为一般惯例,我会同意这本书。


好吧,我现在不知道,因为即使在这种情况下,您也描述了我将明确使用池的(在阅读本文之前),我也会有开销.1)处理池的新结构2)获取/释放对象的同步来自池3)维护池等。因此,我现在认为可能没有用例,它没有用,除了例如缓存一个套接字而不是每次都打开一个新的套接字来连接到服务器。这是因为网络延迟和非实例化创建开销
user10326

@ user10326是的。我看到打开套接字是实例化开销的一部分,如果它是类的工作,并且必须在构造函数中初始化,那么延迟和IO的影响就是您所关心的。
杰里米
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.