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

Mat*_*son 6 java static static-methods

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

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我要创建一个ExampleClass. 该实例是否包含我创建的静态方法和/或字段的代码?

我有一个对象将代表我数据库中一行的一些数据。我想从数据库中的每一行创建这些对象的列表。数据库有数千行。我正在创建的静态方法将在将数据库中的值放入对象构造函数之前对其进行格式化。

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

Pat*_*ick 5

不,静态方法和字段在类的实例中不占用空间。

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

当你创建一个对象的新实例时,它的各种方法的代码没有被“分配”,因为它已经以编译形式存在于你的程序中。


rzw*_*oot 5

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

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

就是这样。不存储静态字段。所有方法都不是,甚至实例方法也不是这只是非静态字段。那个指向单例的指针?只有一种数据结构代表整个虚拟机的ExampleClass。有 1000 个 ExampleClass 实例吗?仍然只有一个。

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

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

class Foo {
    public void hello() {}
}
Run Code Online (Sandbox Code Playgroud)

基本上是语法糖:

class Foo {
    public static void hello(Foo receiver) {}
}
Run Code Online (Sandbox Code Playgroud)

但请注意,实例方法进行动态分派,而静态方法则不进行动态分派。这应该可以解释为什么方法(甚至实例方法)不占用每个实例的任何“内存”。全班仅限一次。同样的技巧不能应用于实例字段。

*) 这过于简单化了,并且 JLS 或 JVMS 都没有指定,但它反映了几乎每个流行的 VM 实际所做的事情。