使用不可变对象编写程序会导致性能问题吗?如果一个给定的对象是不可变的,我们需要以某种方式改变它的状态,我们必须将它映射到一个状态稍有改变的新对象.因此,我们会发现自己处于这样一种情况:我们创建了大量的对象,这些对象会占用内存,而且据我所知,对垃圾收集器来说可能会有问题.我所描述的是发生了什么,还是在这个话题上有一些我不知道的方面?

推荐答案

当你反复修改一个可变对象时,它产生的垃圾可能比反复构造新的不可变对象来表示中间状态要少.但是,使用不可变对象仍然不一定会带来性能问题,有几个原因:

  • 在典型的应用程序中,这种情况只是偶尔发生,与其他不可变对象不受影响甚至不会获胜的应用相比.最值得注意的是:

    • getter在返回不可变对象时不需要创建防御副本
    • 如果不可变,setter可以通过引用存储传入参数
    • 验证方法可以以简单(或天真)的方式实现,而不必处理先判断后行动的问题,因为不可变的对象在判断和后续使用之间无法更改
    • 可以安全地共享不可变的对象,以实际减少创建的对象或使用的内存量
  • 垃圾收集对性能的影响往往被高估.我们经常使用不可变类型java.lang.String,受益于上述优势.字符串也是最常用的哈希映射键之一.

    对于重复字符串操作的场景,有可变伴生类StringBuilder,但它的主要优势是分配对象的数量为not.字符串构造的问题是,每个对象都必须创建所包含字符的副本,因此在每一步构造新字符串时,重复连接字符等操作会导致二次时间复杂度.在必要时,StringBuilder仍然会在引擎盖下重新分配其缓冲区,但会出现非线性增长,这会产生重复串联的摊销线性时间复杂度.

    正如在this answer中所解释的,垃圾收集的成本主要取决于仍然存在的对象,因此临时对象通常不会对垃圾收集产生太大影响,除非您创建了过多的临时对象.但是,即使是后一种情况,也只有当您有实际的性能问题,并且一个公正的分析工具证明特定的分配站点确实是罪魁祸首时,才应该解决.

  • 复杂的应用程序可能需要处理撤销/重做或其他版本控制功能,这些功能无论如何都需要保留备用应用程序状态的副本.此时,使用不可变对象实际上可能会成为一种优势,因为您不需要复制在两个版本的应用程序状态之间没有变化的对象.更改的应用程序状态可能包含99%与前一状态相同的对象.

  • 这个话题(再次)流行的一个重要原因是,不可变对象提供了实现高效和正确并行处理的最简单方法.不需要获取锁来防止不一致的修改.如上所述,没有必要担心先判断后行动的问题.此外,当应用程序状态可以表示为对复合不可变对象的单个引用时,对并发更新的判断简化为简单的引用比较.与this answer相比也是如此.

因此,不可变对象有很多性能优势,可以弥补缺点(如果有的话).同时,通过临时处理操作的可变对象,同时保持整体不变行为的既定解决方案,可以修复潜在的瓶颈.

Java相关问答推荐

如何在多个设备上同时运行Android Studio的项目?

如何使用Spring Data从MongoDB文档中包含的数组中 Select 单个元素?

如何在Java中对自定义协议进行主机名验证?

Java SSLocket查明客户端是否发送了证书

获取拦截器内部的IP地址

Spring Webocket:尽管凭据设置为False,但MLhttpsify和Fetch请求之间的CORS行为存在差异

Android视图覆盖不阻止点击它后面的控件

我的scala文件失败了Scala.g4 ANTLR语法

强制Mockito返回null而不是emtpy list

为什么我们不能实现两个接口,其中一个接口有相同的签名,其中一个接口有默认的实现在java?'

路径映射未发生

SpringBootreact 式Web应用程序的Spring Cloud Configer服务器中的资源控制器损坏

由于我在Main方法中关闭了 scanner ,但在该方法中创建了一个新的 scanner ,因此出现了错误

Com.example.service.QuestionService中的构造函数的参数0需要找不到的类型为';com.example.Dao.QuestionDao;的Bean

在Spring Boot中使用哪个Java类来存储创建时间戳?

Helidon 4和Http API

JOOQ中的子查询使用的是默认方言,而不是配置的方言

JavaFX:无论何时显示应用程序,如何更改组件/ node 位置?

如何修复Spring Boot应用程序中的RestDocumentationGenerationException:java.io.FileNotFoundException:/curl-request.adoc(只读文件系统)?

HBox内部的左对齐按钮(如果重要的话,在页码内)