静态方法和字段是否在它们定义的 class 的实例中占用内存?

Do Static Methods and Fields take up memory in an instance of the class they are defined in?

例如,如果我要创建以下 class:

public Class ExampleClass {
  
  private static String field1;
  private String field2;

  public ExampleClass(String field2) {
    this.field2 = field2;
  }

  public static staticMethodExample(String field1) {
    this.field1 = field1;
  }
}

如果我要创建 ExampleClass 的实例。该实例是否包含我创建的静态方法 and/or 字段的代码?

我有一个对象将代表我数据库中某行的一些数据。我想从数据库中的每一行创建这些对象的列表。数据库有数千行。我正在创建的静态方法会先格式化数据库中的值,然后再将它们放入对象构造函数中。

我不想通过将方法存储在对象的每个实例中来使代码膨胀。因此,如果静态方法确实在对象的每个实例中占用 space,那么我宁愿创建一个单独的 class 名称,如 ExampleClassBuilder。并将格式化静态方法放在那里。

静态变量和方法不会增加 Object 实例的大小。这些字段和方法实际上是添加到 Class 对象中的。

不,静态方法和字段不在 class 的实例中采用 space。

你在这里制造了一些混乱。编译程序时,每个方法(静态或非静态)的代码都“存储”在编译后的程序中(在 Java 的情况下,在 .class 文件中)。当你执行一个程序时,static 成员 - “属于”class,就像你问题中的 field1 - 为你的整个程序分配一次。其他“正常”class 成员 - 如您问题中的 field2 - 为您创建的每个新实例分配。

当您创建对象的新实例时,其各种方法的代码并未“分配”,因为它已经以编译形式存在于您的程序中。

否: 当我们创建一个静态变量或方法时,它存储在堆上的特殊区域:Metaspace(for Java 8 and Permgen for旧版本)。

每当加载 class 时 static 变量 以及 方法 被加载到内存中允许任何instance(discouraged) 或直接使用它们。所以,任何对象都可以改变变量的值,而且它们也可以在不创建实例的情况下被操纵class。 查看更多https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html

此 class 实例的堆上数据大致如下 *:

size (bytes) | description
--------------------------
8            | A pointer to the singleton object representing ExampleClass
8            | A pointer to the value stored in field2

就是这样。不存储静态字段。而且none的方法都是,连实例方法都没有!这只是 non-static 字段。那个指向单例的指针?只有一个数据结构表示整个 VM 的 ExampleClass 是什么。有 1000 个 ExampleClass 实例?还是只有一个。

这些大小可以更小,具体取决于 VM 配置。

那么 java 怎么知道 ExampleClass 的实例有一个名为 instanceMethod 的方法?通过跟踪指向描述 ExampleClass 本身的数据结构的指针,并注意到它有那个。这个:

class Foo {
    public void hello() {}
}

基本上是语法糖:

class Foo {
    public static void hello(Foo receiver) {}
}

但请注意实例方法进行动态分派而静态方法不进行。这应该可以解释为什么每个实例的方法(甚至实例方法)都没有占用任何 'memory' 。整个 class 只有一次。同样的技巧不能应用于实例字段。

*) 这是高度简化的,JLS 或 JVMS 均未指定,但它反映了几乎所有流行的 VM 实际所做的事情。