java - 使用不可变对象(immutable对象)的程序性能

标签 java performance garbage-collection immutability

使用不可变对象(immutable对象)编写程序会导致性能问题吗?如果一个给定的对象是不可变的,而我们需要以某种方式改变它的状态,我们必须将它映射到一个状态略有改变的新对象。因此,我们会发现自己处于创建大量占用内存的对象的情况,据我所知,这可能会给垃圾收集器带来问题。我所描述的情况是否正在发生,或者是否存在我不知道的关于该主题的某些方面?

最佳答案

当您重复修改一个可变对象时,与重复构造新的不可变对象(immutable对象)来表示中间状态相比,它可能会产生更少的垃圾。但是使用不可变对象(immutable对象)仍然不一定会带来性能问题有几个原因:

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

    • getter 在返回不可变对象(immutable对象)时不需要创建防御副本
    • 如果不可变,setter 可以通过引用存储传入的参数
    • 验证方法可以以简单(或幼稚)的方式实现,无需处理先检查后执行的问题,因为不可变对象(immutable对象)在检查和后续使用之间不会发生变化
    • 可以安全地共享不可变对象(immutable对象),以实际减少创建对象或使用内存的数量
  • 垃圾回收对性能的影响常常被高估。我们经常使用不可变类型 java.lang.String,受益于上述优点。字符串也是最常用的 HashMap 键之一。

    对于重复字符串操作的场景,有一个可变的伴随类 StringBuilder,但它的主要优点是关于分配对象的数量。字符串构造的问题是每个对象都必须创建包含字符的副本,因此在每一步构造新字符串时,重复连接字符等操作会导致二次时间复杂度。 StringBuilder 仍会在必要时在后台重新分配其缓冲区,但会出现非线性增长,从而为重复串联产生线性时间复杂度。

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

  • 复杂的应用程序可能必须处理撤消/重做或其他版本控制功能,无论如何都需要保留替代应用程序状态的副本。在这一点上,使用不可变对象(immutable对象)实际上可能成为一个优势,因为您不需要复制在两个版本的应用程序状态之间没有变化的对象。更改后的应用程序状态可能包含 99% 与先前状态相同的对象。

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

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

关于java - 使用不可变对象(immutable对象)的程序性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71455345/

相关文章:

java - SerialversionID 在 Java 反序列化中如何工作

java - 使用仿射变换旋转多边形

r - 如何按组加速子集

java - 除了 hibernate-validator 之外,LocalValidatorFactoryBean 的实现?

java - 为什么我们在序列化过程中使用 serialVersionUID

windows - 如何在 perfmon 工具中保存计数器?

java - Hibernate、JDBC 和 Java 在大中型结果集上的性能

c# - 如果我不收集垃圾,为什么服务器垃圾收集速度更快?

Java ConcurrentMarkSweep 垃圾收集器未发生

android - Android中引起的onLowMemory