如果没有访问类,静态init是否保证不运行?

Tho*_*alc 5 java android static-methods dalvik lazy-initialization

我知道有很多这方面的主题和资源,但我想知道一个非常具体的问题(可能需要很长时间才能检查所有来源以获得明确答案).

我知道JVM/Dalvik保证当你访问类的静态字段时(final static原始值除外),类的静态字段已经初始化.反之亦然吗?如果我从来没有在所有访问类(例如,因为switch-case在另一个静态方法的代码永远不会达到一定的分支),是它保证了虚拟机并没有初始化这个类的静态?

假设我有一个这样的类:

public class Boo {
      public static int[] anything = new int[] { 2,3,4 };
      private static int[] something = new int[] { 5,6,7 }; // this may be much bigger as well

      public static final int[] getAndClear() {
           int[] st = something;
           something = null;
           return st;
      }
}
Run Code Online (Sandbox Code Playgroud)

我的应用程序是一个非常特殊的应用程序(在某些方面不典型),并且它可能包含数百个类,例如Boo(由代码生成器生成),其中something可能是不同元素计数的数组(因此它可能包含很多元素,如好吧有时候).

根据应用程序输入,许多这些预生成的类可能永远不会被访问.我希望很多int[]对象被不必要地初始化,占用大量内存.

Mik*_*uel 9

我知道JVM/Dalvik保证在访问类的静态字段时(最终的静态原始值除外),类的静态字段已经初始化.

这通常是正确的,但由于内联不变,某些静态字段不是这种情况.在

class A {
  public static final String FOO = "foo";

  static { System.out.println("loaded A"); }
}

public class B {
  public static void main(String... argv) {
    System.out.println("Got " + A.FOO);
  }
}
Run Code Online (Sandbox Code Playgroud)

JVM将打印"Got foo"但不会打印"已加载A".事实上,即使A.class不在类路径上,B也会运行,但编译时至少有一个A.javaA.class必须可用B.java.


反之亦然吗?如果我根本不访问类(例如,因为另一个静态方法中的switch-case代码永远不会到达某个分支),是否可以保证VM不初始化此类的静态?

是.JLS列出了类加载和初始化发生的确切条件,因此JVM实现无法急切地加载或初始化类.

12.4.1是感兴趣的章节.

12.4.1.发生初始化时

类或接口类型T将第一次出现以下任何一个之前立即初始化:

  1. T是一个类,并且创建了T的实例.
  2. T是一个类,并且调用由T声明的静态方法.
  3. 分配由T声明的静态字段.
  4. 使用由T声明的静态字段,该字段不是常量变量(第4.12.4节).
  5. T是顶级类(第7.6节),并且执行在词典内嵌套在T(第8.1.3节)内的断言语句(第14.10节).

"紧接着之前"的措辞禁止任何渴望,并强制当两个类都尝试执行上述操作之一时会发生什么 - 有一个与已加载但未初始化的类相关联的锁,并且都等到第一次获取该类的线程lock执行初始化.

的"和字段不是一个常数变量(§4.12.4)"的措辞是例外,我通过示出的规则class B使用A.FOO的上方.


public static final int[] getAndClear() { ... }
Run Code Online (Sandbox Code Playgroud)

应该是synchronized因为否则两个线程可能都获得相同的数组而不是一个接收null.我上面提到的类加载器锁没有保护getAndClear.