Java Records 是否真的通过类似的类声明来节省内存,或者它们更像是语法糖?

Cla*_*ick 9 java memory-footprint java-14 java-record

我希望 Java 14 记录实际上比类似的数据类使用更少的内存。

他们或内存使用相同吗?

Nam*_*man 7

添加到@lugiorgi执行的基本分析以及我可以想出的分析字节码的类似显着差异是在toString,equals和的实现中hashcode

一方面,具有重写Object类 API的现有类看起来像

public class City {
    private final Integer id;
    private final String name;
    // all-args, toString, getters, equals, and hashcode
}
Run Code Online (Sandbox Code Playgroud)

产生如下字节码

 public java.lang.String toString();
    Code:
       0: aload_0
       1: getfield      #7                  // Field id:Ljava/lang/Integer;
       4: aload_0
       5: getfield      #13                 // Field name:Ljava/lang/String;
       8: invokedynamic #17,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/Integer;Ljava/lang/String;)Ljava/lang/String;
      13: areturn

  public boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: if_acmpne     7
       5: iconst_1
       6: ireturn
       7: aload_1
       8: ifnull        22
      11: aload_0
      12: invokevirtual #21                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
      15: aload_1
      16: invokevirtual #21                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
      19: if_acmpeq     24
      22: iconst_0
      23: ireturn
      24: aload_1
      25: checkcast     #8                  // class edu/forty/bits/records/equals/City
      28: astore_2
      29: aload_0
      30: getfield      #7                  // Field id:Ljava/lang/Integer;
      33: aload_2
      34: getfield      #7                  // Field id:Ljava/lang/Integer;
      37: invokevirtual #25                 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
      40: ifne          45
      43: iconst_0
      44: ireturn
      45: aload_0
      46: getfield      #13                 // Field name:Ljava/lang/String;
      49: aload_2
      50: getfield      #13                 // Field name:Ljava/lang/String;
      53: invokevirtual #31                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      56: ireturn

  public int hashCode();
    Code:
       0: aload_0
       1: getfield      #7                  // Field id:Ljava/lang/Integer;
       4: invokevirtual #34                 // Method java/lang/Integer.hashCode:()I
       7: istore_1
       8: bipush        31
      10: iload_1
      11: imul
      12: aload_0
      13: getfield      #13                 // Field name:Ljava/lang/String;
      16: invokevirtual #38                 // Method java/lang/String.hashCode:()I
      19: iadd
      20: istore_1
      21: iload_1
      22: ireturn
Run Code Online (Sandbox Code Playgroud)

另一方面,相同的记录表示

record CityRecord(Integer id, String name) {}
Run Code Online (Sandbox Code Playgroud)

产生的字节码少至

 public java.lang.String toString();
    Code:
       0: aload_0
       1: invokedynamic #19,  0             // InvokeDynamic #0:toString:(Ledu/forty/bits/records/equals/CityRecord;)Ljava/lang/String;
       6: areturn

  public final int hashCode();
    Code:
       0: aload_0
       1: invokedynamic #23,  0             // InvokeDynamic #0:hashCode:(Ledu/forty/bits/records/equals/CityRecord;)I
       6: ireturn

  public final boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: invokedynamic #27,  0             // InvokeDynamic #0:equals:(Ledu/forty/bits/records/equals/CityRecord;Ljava/lang/Object;)Z
       7: ireturn
Run Code Online (Sandbox Code Playgroud)

注意:对于我在生成的访问器和构造函数字节代码上所观察到的,它们的表示形式相似,因此也从这里的数据中排除。


lug*_*rgi 2

我做了一些快速而肮脏的测试如下

public record PersonRecord(String firstName, String lastName) {}
Run Code Online (Sandbox Code Playgroud)

public record PersonRecord(String firstName, String lastName) {}
Run Code Online (Sandbox Code Playgroud)

编译后的记录文件大小为1.475 字节,类文件大小为1.643 字节。大小差异可能来自不同的 equals/toString/hashCode 实现。

也许有人可以进行一些字节码挖掘......