【发布时间】:2010-09-28 22:11:14
【问题描述】:
是否应该尽可能频繁地重用 Java 对象?或者我们应该只在它们“重量级”时重用它,即有与之关联的操作系统资源?
互联网上所有的老文章都尽可能多地谈论对象重用和对象池,但是我读过最近的文章说new Object()现在已经高度优化(10条指令)并且对象重用没有那么大以前是这样的。
目前的最佳做法是什么?你们是如何做到的?
【问题讨论】:
标签: java
是否应该尽可能频繁地重用 Java 对象?或者我们应该只在它们“重量级”时重用它,即有与之关联的操作系统资源?
互联网上所有的老文章都尽可能多地谈论对象重用和对象池,但是我读过最近的文章说new Object()现在已经高度优化(10条指令)并且对象重用没有那么大以前是这样的。
目前的最佳做法是什么?你们是如何做到的?
【问题讨论】:
标签: java
我让垃圾收集器为我做这种决定,我唯一一次用新分配的对象达到堆限制是在运行一个错误的递归算法几秒钟后生成 3 * 27 * 27.. . 尽可能快地创建新对象。
尽最大努力提高可读性和封装性。有时重用对象可能很有用,但通常您不必担心。
【讨论】:
如果您非常大量使用并且构造成本高昂,您应该尽可能多地重复使用它们。
如果你的对象非常小,而且创建起来很便宜(比如 Object ),你应该创建新的。
例如,连接数据库是池化的,因为创建一个新的成本高于创建 .. mmhh new Integer 的成本。
所以你的问题的答案是,重用时重用 AND 经常使用(不值得汇集一个只使用两次的 3 mb 对象)
编辑:
另外,来自 Effective Java:Favor Immutability 的这篇文章值得一读,可能适用于你的情况。
【讨论】:
让垃圾收集器完成它的工作,它可以被认为比你的代码更好。
除非分析器证明它有罪。甚至不要用常识来试图找出它什么时候是错的。在不寻常的情况下,即使是像字节数组这样廉价的对象也可以更好地池化。
【讨论】:
对象创建很便宜,是的,但有时还不够便宜。
如果你快速连续地创建很多(我的意思是很多)临时对象,垃圾收集器的成本是相当可观的。然而,即使有一个好的分析器,您也不一定能轻易看到成本,因为现在垃圾收集器的工作时间间隔很短,而不是阻塞整个应用程序一两秒钟。
我在项目中获得的大部分性能改进来自于避免对象创建或通过积极缓存避免整个工作(包括对象创建)。无论对象有多大或多小,创建它并管理它的引用和堆结构仍然需要时间。 (当然,清理和内部堆碎片整理/复制也需要时间。)
我不会开始热衷于以全部成本来避免创建对象,但是如果您在内存分析器中看到拼图模式,则意味着您的垃圾收集器正在承担重任。如果您的垃圾收集器使用 CPU,则 CPI 对您的应用程序不可用。
关于对象池:正确执行它并且不会遇到内存泄漏或无效状态或花费更多时间进行管理而不是节省的时间是很困难的。所以我从来没有使用过这种策略。
我的策略是简单地争取不可变的对象。不可变的东西可以轻松缓存,因此有助于保持系统简单。
但是,无论您做什么:请务必先使用分析器检查您的热点。过早的优化是万恶之源。
【讨论】:
经验法则应该是使用您的常识并在对象的创建消耗重要资源(例如 I/O、网络流量、数据库连接等...
如果它只是创建一个新的String(),忘记重用,你将一无所获。代码可读性优先。
【讨论】:
如果出现性能问题,我会担心。首先做有意义的事情(你会用原语做这个),如果你然后运行一个分析工具并发现它是新的导致你的问题,开始考虑预分配(即当你的程序没有做很多工作时)。
顺便说一句,重复使用对象听起来像是一场等待发生的灾难:
SomeClass someObject = new SomeClass();
someObject.doSomething();
someObject.changeState();
someObject.changeOtherState();
someObject.sendSignal();
// stuff
//re-use
someObject.reset(); // urgh, had to put this in to support reuse
someObject.doSomethingElse(); // oh oh, this is wrong after calling changeOtherState, regardless of reset
someObject.changeState(); // crap, now this is wrong but it's not obvious yet
someObject.doImportantStuff(); // what's going on?
【讨论】:
对象创建肯定比以前更快。 JDK 5 及更高版本中的新一代 GC 也是改进。
我不认为这两种方法都可以免费地创建过多的对象,但它们确实降低了对象池的重要性。我认为池化对于数据库连接是有意义的,但我不会为我自己的域对象尝试它。
重用重视线程安全。您需要仔细考虑以确保您可以安全地重复使用对象。
如果我认为对象重用很重要,我会使用 Terracotta、Tangersol、GridGain 等产品,并确保我的服务器有大量可用内存。
【讨论】:
第二个上面的cmets。
不要尝试第二次猜测 GC 和热点。对象池可能曾经有用过,但现在它不是那么有用,除非您在谈论数据库连接或独特的系统资源。
只需尝试编写干净简单的代码,就会对 Hotspot 的功能感到惊讶。
为什么不使用 VisualVM 或分析器来查看您的代码?
【讨论】: