首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不可变对象如何帮助减少垃圾收集造成的开销?

不可变对象如何帮助减少垃圾收集造成的开销?
EN

Stack Overflow用户
提问于 2016-02-13 19:29:20
回答 3查看 7.4K关注 0票数 28

我是一个新手,我读过关于垃圾收集的前两个答案这里

现在,与使用现有对象(在多线程应用程序中)相比,即使程序员必须创建新对象,也有理由使用不可变对象,本教程说,创建对象的成本是由垃圾收集导致的内存开销减少以及消除代码以保护可变对象不受线程干扰和内存一致性错误所弥补的:

对象创建的影响常常被高估,并且可以被一些与不可变对象相关的效率所抵消。这包括由于垃圾收集减少了的开销,以及消除了保护可变对象不受损坏所需的代码。

问题是怎么回事?垃圾收集与对象的可变性或不可变性有什么关系?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-02-13 19:36:13

有时,当对象不可变时,分配的时间会更少。

简单例子

代码语言:javascript
复制
 Date getDate(){
   return copy(this.date);
 }

每次我共享Date时,我都必须复制它,因为它是可变的,或者调用者可以修改它。如果getDate经常被调用,那么分配率就会急剧增加,这将给GC带来压力。

另一方面,Java-8日期是不可变的。

代码语言:javascript
复制
LocalDate getDate(){
  return this.date;
}

注意,我不需要复制日期(分配一个新的对象),因为它是不可变的(我很高兴与您共享该对象,因为我知道您不能更改它)。

现在,您可能会想,如何才能将它应用于“有用”或复杂的数据结构,而不会导致大量的分配(由于防御性的副本),您是完全正确的,但是有一种叫做functional programmingpersistent data structures的艺术(即,您有一种错觉,认为它是一个新副本,实际上复制与原始副本共享了很多)。

大多数函数式语言(我所知道的所有语言)都是垃圾收集的,这一点也不足为奇。

票数 21
EN

Stack Overflow用户

发布于 2016-02-13 20:20:25

如果要跨上下文共享不可变对象(不信任的.e.g调用代码)或线程安全,则不需要防御性副本。这可能意味着,从垃圾的角度来看,不可变对象的读取可能更低。

另一方面,每次更改不可变对象时,您都必须创建新对象,不管这是否需要。在这方面,不可变对象可以创建更多的垃圾。

真正的问题是,您是否正在进行大量的读取,或者根据使用情况进行大量的写入(或混合),不可变对象可以保存对象或创建更多的对象,因此根据特定的用例使用不可变或可变的对象是有意义的。

注意:大多数情况下,正确性比性能重要得多,而且在一般情况下,不可变对象具有更高的开销IMHO,但是使用不可变对象来证明数据模型的正确性要容易得多,并且值得使用不变的对象来进行清晰和简单的推理。

票数 16
EN

Stack Overflow用户

发布于 2016-02-13 22:56:28

这篇文章中,Brian很好地解释了这一点。基本上,它与垃圾收集器的工作方式有关。如果新对象引用旧对象,则要做的工作要比引用旧对象要做的工作少。

摘自链接文章的示例类如下:

代码语言:javascript
复制
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对象)。

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35384393

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档