使用不可变对象编写程序会导致性能问题吗?如果一个给定的对象是不可变的,我们需要以某种方式改变它的状态,我们必须将它映射到一个状态稍有改变的新对象.因此,我们会发现自己处于这样一种情况:我们创建了大量的对象,这些对象会占用内存,而且据我所知,对垃圾收集器来说可能会有问题.我所描述的是发生了什么,还是在这个话题上有一些我不知道的方面?
使用不可变对象编写程序会导致性能问题吗?如果一个给定的对象是不可变的,我们需要以某种方式改变它的状态,我们必须将它映射到一个状态稍有改变的新对象.因此,我们会发现自己处于这样一种情况:我们创建了大量的对象,这些对象会占用内存,而且据我所知,对垃圾收集器来说可能会有问题.我所描述的是发生了什么,还是在这个话题上有一些我不知道的方面?
当你反复修改一个可变对象时,它产生的垃圾可能比反复构造新的不可变对象来表示中间状态要少.但是,使用不可变对象仍然不一定会带来性能问题,有几个原因:
在典型的应用程序中,这种情况只是偶尔发生,与其他不可变对象不受影响甚至不会获胜的应用相比.最值得注意的是:
垃圾收集对性能的影响往往被高估.我们经常使用不可变类型java.lang.String
,受益于上述优势.字符串也是最常用的哈希映射键之一.
对于重复字符串操作的场景,有可变伴生类StringBuilder
,但它的主要优势是分配对象的数量为not.字符串构造的问题是,每个对象都必须创建所包含字符的副本,因此在每一步构造新字符串时,重复连接字符等操作会导致二次时间复杂度.在必要时,StringBuilder
仍然会在引擎盖下重新分配其缓冲区,但会出现非线性增长,这会产生重复串联的摊销线性时间复杂度.
正如在this answer中所解释的,垃圾收集的成本主要取决于仍然存在的对象,因此临时对象通常不会对垃圾收集产生太大影响,除非您创建了过多的临时对象.但是,即使是后一种情况,也只有当您有实际的性能问题,并且一个公正的分析工具证明特定的分配站点确实是罪魁祸首时,才应该解决.
复杂的应用程序可能需要处理撤销/重做或其他版本控制功能,这些功能无论如何都需要保留备用应用程序状态的副本.此时,使用不可变对象实际上可能会成为一种优势,因为您不需要复制在两个版本的应用程序状态之间没有变化的对象.更改的应用程序状态可能包含99%与前一状态相同的对象.
这个话题(再次)流行的一个重要原因是,不可变对象提供了实现高效和正确并行处理的最简单方法.不需要获取锁来防止不一致的修改.如上所述,没有必要担心先判断后行动的问题.此外,当应用程序状态可以表示为对复合不可变对象的单个引用时,对并发更新的判断简化为简单的引用比较.与this answer相比也是如此.
因此,不可变对象有很多性能优势,可以弥补缺点(如果有的话).同时,通过临时处理操作的可变对象,同时保持整体不变行为的既定解决方案,可以修复潜在的瓶颈.