首页 > 解决方案 > 从垃圾回收的角度看 Java 不可变类

问题描述

随着 Java 8 的到来,我们得到了 lambda 和流,我们都开始做“函数式编程”。函数式编程的特性之一是不变性。在java中创建不可变对象并不是那么容易,但它是可能的。

这是一个简单的例子:

public class Test {

    public static void main(String []args){

       PersonService personService = new PersonService(); 
       
       Person p = Person.of("XYZ", 12);
       Person p2 = personService.setName(p, "New Name");
       Person p3 = personService.setAge(p2, 45);
       
       System.out.println(p);
       System.out.println(p2);
       System.out.println(p3);
    }
}

class PersonService {
   
   public Person setName(Person existingPerson, String name) {
       return Person.of(name, existingPerson.getAge());
   }
   
   public Person setAge(Person existingPerson, int age) {
       return Person.of(existingPerson.getName(), age);
   }
}

final class Person {
   private final String name;
   private final int age; 
   
   private Person(String name, int age) {
       this.name = name;
       this.age = age;
   }
   
   public String getName() {
       return this.name;
   }
   
   public int getAge() {
       return this.age;
   }
   
   public static Person of(String name, int age) {
       return new Person(name, age);
   }
   
   public String toString() {
       return name + " " + age;
   }
}

许多人认为垃圾收集收集和丢弃死对象。实际上,Java 垃圾收集正在做相反的事情!活动对象被跟踪,其他一切都被指定为垃圾。此外,Java 垃圾收集基于这样的假设,即大多数对象都是年轻的(在创建后不久就不再引用,因此可以安全地销毁)。大多数开发人员过去认为在 Java 中创建对象很慢,应该尽可能避免创建新对象。事实上,它确实非常便宜和快速。然而,代价高昂的是不必要地创建长期存在的对象,最终可能会填满堆并导致问题。

那么对于 GC 来说使用不可变对象会更好吗,因为它们的寿命不会很长?

标签: javafunctional-programminggarbage-collectionimmutability

解决方案


那么对于 GC 来说使用不可变对象会更好吗,因为它们的寿命不会很长?

这是无法回答的。你说的好像只有一个 GC,但它不是这样工作的:有许多可用的 GC 实现,在启动时你选择你想要使用的那个。大多数可用的实现也是可广泛配置的。

例如,新的 zgc 的目标是不中断正在运行的代码(收集发生在您的 VM 继续运行时;很少出现“GC 暂停”,并且当它们发生时,它们不会持续很长时间,但作为权衡,ZGC 通常会占用更多 CPU与其他 GC 相比,在 VM 的生命周期内循环,这就是为什么您可以选择所需的 GC。对于所有可预见的工作负载,没有一个固定的答案)。目前(来源:ZGC 上的 Java 播客片段)它对分代系统的作用并不大,这意味着使用 ZGC 创建大量垃圾可能比使用可变对象要慢。

正如您所说,许多 GC 将使用分代技术,因此可以很好地处理大量的短期垃圾 - 从理论上讲,收集大量短期垃圾比使用此类 GC 收集一些长期垃圾对象更快。

有很多短暂的垃圾会“更快”吗?不。

是“慢”吗?不。

关键是要大想:我们(我们人类)知道如何调整垃圾收集器,以便他们能够有效地处理大量短命的垃圾。再加上 java 作为一门语言显然正在转向(因此,从事上述 GC 工作的工程师意识到)一个大量现实生活中的应用程序将拥有大量短命垃圾的世界这一事实。

这意味着,如果它是一个问题,它会得到解决。

因此,您应该编程以使您的代码“干净”。这是一个狡猾的词,所以我将其定义为:易于维护、易于阅读、易于测试、面对不断变化的需求易于更改、易于连接到其他系统并适用于其他用途。

如果这样写代码你最终会制造大量的短命垃圾?没关系。不要基于关于某些特定 GC impl 设计方式的单一报告,也不要因将某种神秘的能力归咎于世代收集器的某种神秘能力比替代品。

如果您的 JVM 太慢或有其他问题,请获取一份报告,如果该报告说大量短期对象确实是问题所在,则调整 GC 设置。如果这没有帮助,你猜怎么着?是时候更改代码了,您必须立即部署,不能等待模糊的梦想成真。这里的关键教训是不要试图预测你最终会在这里并相应地编写代码,因为我们只是凡人,这是超越人类的某种神一般的力量。否:教训是,编写“干净”的代码(如上定义),因为干净的代码很容易适应特定的性能需求。这与编写代码形成鲜明对比,因为您关心的不是干净的代码(例如:担心制作少量对象或制作大量短命的对象,


推荐阅读