我是新手,我从前两个答案here读到了关于垃圾收集的内容。
现在证明使用不可变对象,即使程序员必须创建新对象,与使用现有对象相比(在多线程应用程序中),this tutorial表示对象创建的成本弥补了通过由于垃圾收集导致的内存开销减少,并且消除了保护可变对象免受线程干扰和内存一致性错误的代码:
对象创建的影响经常被高估,并且可以 与不可变对象相关的一些效率抵消。 这些包括由于垃圾收集而减少的开销,以及 消除保护可变对象所需的代码 损坏。
问题是怎么样?垃圾收集与对象的可变性或不变性有什么关系?
答案 0 :(得分:15)
有时,当对象不可变时,你会少分配。
简单示例
Date getDate(){
return copy(this.date);
}
每次我分享它时都必须复制Date
,因为它是可变的,或者调用者能够改变它。如果getDate
被大量调用,分配率将急剧增加,这将对GC
施加压力
另一方面,Java-8日期是不可变的
LocalDate getDate(){
return this.date;
}
请注意,由于不变性,我不需要复制日期(分配新对象)(我很乐意与您分享对象,因为我知道您无法改变它)。
现在你可能会想我怎样才能将它应用到"有用的"或者没有造成大量分配的复杂数据结构(由于防御性副本),你是绝对正确的,但是有一种叫做functional programming
和persistent data structures
的艺术(即:你会产生一种错觉,即它是一种&新副本实际上副本与原始副本分享很多)。
大多数函数式语言(我所知道的所有语言)都是垃圾收集的,不应该感到惊讶。
答案 1 :(得分:9)
如果您要跨越上下文(.e.g调用您不信任的代码)或线程安全共享它们,则不可变对象不需要防御性副本。这可能意味着不可变对象的读取在垃圾方面可能更低。
另一方面,每次更改不可变对象时,无论是否需要,都必须创建新对象。在这方面,不可变对象可以创建更多的垃圾。
真正的问题是你是在进行大量的读取还是大量的写入(或混合)取决于使用不可变对象可以保存对象或创建更多对象,因此使用基于的Immutable或Mutable对象是有意义的你的具体用例。
注意:大多数时候,正确性远远超过性能,而且一般来说,不可变对象具有更高的开销恕我直言,使用Immutable对象更容易证明数据模型的正确性,值得一看使用不可变对象只是为了清晰和易于推理。
答案 2 :(得分:7)
在this article中,Brian Goetz很好地解释了这一点。基本上,它与垃圾收集器的工作方式有关。如果新对象引用旧对象而不是反之亦然,则可以做的工作较少。
摘自以下示例类的链接文章:
public class MutableHolder {
private Object value;
public Object getValue() { return value; }
public void setValue(Object o) { value = o; }
}
public class ImmutableHolder {
private final Object value;
public ImmutableHolder(Object o) { value = o; }
public Object getValue() { return value; }
}
在大多数情况下,更新持有者对象以引用a 不同的对象,新的指称是一个年轻的对象。如果我们更新一个
MutableHolder
通过致电setValue()
,我们创造了一个情境 旧对象引用较年轻的对象。另一方面,通过 相反,创建一个新的ImmutableHolder
对象,一个更年轻的对象 引用旧的。后一种情况,大多数对象指向较旧的对象,是 对世代垃圾收集器更加温和。如果一个 生活在老一代的
MutableHolder
变异了 必须扫描卡上包含MutableHolder
的对象 适用于下一个小型收藏中的老少参考。对长寿命容器对象使用可变引用 增加了在收集时跟踪从老到年的参考文献所做的工作 时间。
逃脱分析
关于因为在需要更改现有对象时实例化新对象而产生大量对象的问题,在最新的JVM中对象分配机制得到了很大改进。
看看escape analysis(链接文章中也提到过)。许多对象不会在堆上分配(但在堆栈上内联/分配),因此GC与它们无关(实际上GC并不知道这些对象存在)。
虽然不仅仅与不变性有关,但是可以在不可变的上下文中更有效地利用转义分析机制(例如Person
对象,在链接的Oracle文档中的方法调用期间不会更改。)