Java 从垃圾回收的角度来看是不可变的 类

Java immutable classes from perspective of garbage collection

随着 Java8 的到来,我们有了 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,因为它们的寿命不会很长?

So is using immutable objects better for GC, because they are not going to live very long ?

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

例如,新的 zgc 的目标是不中断 运行 代码(收集发生在您的 VM 只保留 运行;少数 'GC pauses',当它们发生时,它们不会持续很长时间,但权衡之下,ZGC 在 VM 的整个生命周期中通常比其他 GC 需要更多的 CPU 周期,这就是为什么您可以选择您想要的 GC。没有一个固定的答案是优越的所有可预见的工作量)。 目前(来源:Inside Java podcast episode on ZGC)它在分代系统方面做的并不多,这意味着使用 ZGC 创建大量垃圾可能比使用可变对象要慢。

正如您所说,许多 GC 将使用分代技术,因此可以很好地处理大量 short-lived 垃圾 - 理论上,收集大量 short-lived 垃圾比收集少量 long-lived 具有此类 GC 的垃圾对象。

是不是'faster'有很多短暂的垃圾?不。

是'slower'吗?不。

关键是要有大局观:我们(我们人类)知道如何调整垃圾收集器,以便他们能够有效地处理成吨的短期垃圾。结合 java 作为一种语言 显然 正在转向(因此,从事上述 GC 工作的工程师意识到)一个充满现实生活的世界应用程序会有很多短暂的垃圾。

这意味着如果它是一个问题,它将被解决。

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

如果这样写代码,你最终会产生大量短暂的垃圾?没关系。不要让 'pre-swayed' 改变代码的工作方式,无论是基于关于某些特定 GC impl 是如何设计的单一报告,也不要通过模糊的胡言乱语,这些胡言乱语将某种神话般的能力归因于分代收集器神奇地比替代方案。

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