【问题标题】:Static initialization, main and premain order of execution [closed]静态初始化,执行的主要和前主要顺序[关闭]
【发布时间】:2020-06-08 21:36:14
【问题描述】:

假设我有以下项目结构:

Inst.java

import java.lang.instrument.Instrumentation;

final public class Inst {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long sizeof(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

常量.java

public class Constants {
    public static final int MAX_LENGTH = 10;
}

SomeClass.java

public class SomeClass {
    private int myVar;

    public SomeClass(final int myVar) {
        this.myVar = myVar;
    }

    public static class StaticClass {
        private static final int SOME_VAR = 1;
    }
}

Main.java

public class Main {
    public static void main(String [] args) {
        int init = Constants.MAX_LENGTH;
        SomeClass clazz = new SomeClass(init);           
    }
}

Oracle 文档声明如下:

Java 虚拟机 (JVM) 初始化后,每个 premain 方法将按照代理指定的顺序调用,然后调用真正的应用程序 main 方法。

在我看来,这意味着 premain 方法在 JVM 初始化后立即调用,因此首先初始化 Inst(在本例中)。

此外,static 在类加载时被初始化(据我所知),或更一般地,在类的第一次引用时被初始化。

所以这意味着,在这个例子中,初始化和执行顺序如下:

  1. JVM初始化后调用premain()
  2. main() 被称为应用程序的入口点
  3. Constants.MAX_LENGTH 已初始化(在 main 中引用)
  4. StaticClass 已初始化(由于正在创建外部类的实例)
  5. SomeClass 的实例已创建

我上面的顺序正确吗?

【问题讨论】:

  • 第一:Constants.MAX_LENGTH是一个常量表达式,which doesn't load the class
  • 对不起。你的问题不清楚。您是在编写“Java 编程语言代理”还是在编写常规 Java 程序?您如何运行问题中的代码?
  • 第二:内部类没有以JLS§12.4.1描述的方式访问,所以没有初始化。
  • 第三:您可以在您的类中添加static {System.out.println("Foo is initialized"); },以查看它们何时加载。
  • @JohannesKuhn Constants.MAX_LENGTH 写在代码中不是最终的,所以它不是一个常数,即使它都是大写的。

标签: java static initialization


【解决方案1】:

正如您所提到的,静态初始化程序在类加载后立即被调用。但是在需要这些类之前加载类。因此,在调用 main() 之前,会加载 Constants 并加载 SomeClass

如果您的代理Inst 需要Constants,则应在Main 之前加载Constants

有趣的是,如果你在依赖中有一个循环,那么当检测到它时循环就会被打破,结果将取决于类的加载顺序。

编辑: 您可以将上面的代码放在文件中并使用java -verbose:class Main 运行它,您会看到加载了很多类,包括加载类的顺序:

C:\temp\java>\opt\jdk-11\bin\java.exe -verbose:class Main
[0.009s][info][class,load] opened: C:\opt\jdk-11\lib\modules
.
.
.
[0.326s][info][class,load] Main source: file:/C:/temp/java/
[0.326s][info][class,load] java.lang.NamedPackage source: jrt:/java.base
[0.327s][info][class,load] java.lang.PublicMethods$MethodList source: jrt:/java.base
[0.327s][info][class,load] java.lang.PublicMethods$Key source: jrt:/java.base
[0.328s][info][class,load] java.lang.Void source: jrt:/java.base
[0.329s][info][class,load] Constants source: file:/C:/temp/java/
[0.330s][info][class,load] SomeClass source: file:/C:/temp/java/
[0.331s][info][class,load] jdk.internal.misc.TerminatingThreadLocal$1 source: jrt:/java.base
[0.331s][info][class,load] java.lang.Shutdown source: jrt:/java.base
[0.332s][info][class,load] java.lang.Shutdown$Lock source: jrt:/java.base

【讨论】:

  • 小吹牛:代理是通过系统类加载器加载的,系统类加载器也加载了main方法。见javadoc for the java.lang.instrument package(搜索appendToClassPathForInstrumentation
  • 啊! But classes are loaded before these are needed. 这是我似乎没有看到的关键部分。因此,原始示例中的应用程序的执行图将是(考虑到 Johannes 的输入):Inst loaded -> premain() called -> Main loaded -> Constants loaded -> Constants static initializations -> SomeClass loaded -> SomeClass static initializations -> main() called,记住在调用 main() 之后可能会出现不同的 classloader。尽管有3 的情况,这是正确的吗?
  • 其实:重读 Johannes 关于常量表达式的评论,不会有Constants static initialization 部分。
  • @user991710 现在您将 final 放入 Constants.MAX_LENGTH 中,然后它被内联并且不调用静态初始化程序。最好的方法是尝试一下,使用命令选项 -verbose:class 运行,添加 System.out.println() 并查看实际发生的情况,因为小的更改可能会产生巨大的影响
猜你喜欢
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
  • 2015-09-05
  • 1970-01-01
  • 2016-01-06
  • 2010-09-17
相关资源
最近更新 更多