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

时间:2012-10-26 18:08:53

标签: java android static-methods dalvik lazy-initialization

我知道有很多关于此的主题和资源,但我想知道一个非常具体的问题(并且可能需要很长时间来检查明确的答案的所有来源)。

我知道JVM / Dalvik保证在访问类的静态字段时(final static原始值除外),类的静态字段已经初始化。反之亦然吗?如果我从不根本访问一个类(例如,因为另一个静态方法中的switch-case代码永远不会到达某个分支),是否保证VM 初始化这个类的静态?

假设我有一个这样的课程:

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;
      }
}

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

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

1 个答案:

答案 0 :(得分: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);
  }
}

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


  

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

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

12.4.1是感兴趣的章节。

  

12.4.1。初始化发生时

     

类或接口类型T将在之前在第一次出现之前初始化:

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

“紧接之前”的措辞禁止任何急切,并强制当两个类都尝试执行上述操作之一时会发生什么 - 有一个与已加载但未初始化的类关联的锁,并且都等到线程首先获取该锁执行初始化。

“和字段不是常量变量(§4.12.4)”“verbiage是class B使用A.FOO上面说明的规则的例外。


public static final int[] getAndClear() { ... }

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