发布于 2016-02-13 19:36:13
有时,当对象不可变时,分配的时间会更少。
简单例子
Date getDate(){
return copy(this.date);
}每次我共享Date时,我都必须复制它,因为它是可变的,或者调用者可以修改它。如果getDate经常被调用,那么分配率就会急剧增加,这将给GC带来压力。
另一方面,Java-8日期是不可变的。
LocalDate getDate(){
return this.date;
}注意,我不需要复制日期(分配一个新的对象),因为它是不可变的(我很高兴与您共享该对象,因为我知道您不能更改它)。
现在,您可能会想,如何才能将它应用于“有用”或复杂的数据结构,而不会导致大量的分配(由于防御性的副本),您是完全正确的,但是有一种叫做functional programming和persistent data structures的艺术(即,您有一种错觉,认为它是一个新副本,实际上复制与原始副本共享了很多)。
大多数函数式语言(我所知道的所有语言)都是垃圾收集的,这一点也不足为奇。
发布于 2016-02-13 20:20:25
如果要跨上下文共享不可变对象(不信任的.e.g调用代码)或线程安全,则不需要防御性副本。这可能意味着,从垃圾的角度来看,不可变对象的读取可能更低。
另一方面,每次更改不可变对象时,您都必须创建新对象,不管这是否需要。在这方面,不可变对象可以创建更多的垃圾。
真正的问题是,您是否正在进行大量的读取,或者根据使用情况进行大量的写入(或混合),不可变对象可以保存对象或创建更多的对象,因此根据特定的用例使用不可变或可变的对象是有意义的。
注意:大多数情况下,正确性比性能重要得多,而且在一般情况下,不可变对象具有更高的开销IMHO,但是使用不可变对象来证明数据模型的正确性要容易得多,并且值得使用不变的对象来进行清晰和简单的推理。
发布于 2016-02-13 22:56:28
在这篇文章中,Brian很好地解释了这一点。基本上,它与垃圾收集器的工作方式有关。如果新对象引用旧对象,则要做的工作要比引用旧对象要做的工作少。
摘自链接文章的示例类如下:
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; }
}在大多数情况下,当holder对象被更新为引用不同的对象时,新的引用是一个年轻的对象。如果我们通过调用
MutableHolder来更新setValue(),那么我们已经创建了一个旧对象引用较年轻对象的情况。另一方面,通过创建一个新的ImmutableHolder对象,一个年轻的对象将引用一个旧的对象。 后一种情况(大多数对象指向旧对象)对于分代垃圾收集器来说要温和得多。如果居住在老一代的MutableHolder发生了变异,则必须在下一次小集合中扫描卡片上包含MutableHolder的所有对象的新旧引用。 对长寿命容器对象使用可变引用会增加在收集时跟踪新旧引用的工作量。
逃逸分析
考虑到创建大量对象是因为每当需要更改现有对象时都会实例化一个新对象,在最新的JVM中,对象分配机制有了很大改进。
看看逃逸分析 (链接文章中也提到过)。许多对象将不会在堆上被分配(而是在堆栈上被内联/分配),所以GC将与它们无关(实际上GC根本不知道这些对象存在)。
虽然不仅与不可变性相关,但可以在不可变的上下文中更有效地使用转义分析机制(例如,在链接的Oracle中的方法调用过程中不更改Person对象)。
https://stackoverflow.com/questions/35384393
复制相似问题