java方法执行jvm做了什么_JVM执行程序时,都发生了什么?

JVM执行程序时,都发生了什么?

当一个Java程序被执行时,JVM都做了什么?今天这篇文章,记录了我的探索过程。让我们开始吧!

下文使用的程序选自Log4j文档中的示例程序。

JDK 版本 :

java version "12" 2019-03-19

Java(TM) SE Runtime Environment (build 12+33)

Java HotSpot(TM) 64-Bit Server VM (build 12+33, mixed mode, sharing)

package self.samson.bu;

// Import log4j classes.

import org.apache.logging.log4j.Logger;

import org.apache.logging.log4j.LogManager;

public class MyApp {

// Define a static logger variable so that it references the

// Logger instance named "MyApp".

private static final Logger logger = LogManager.getLogger(MyApp.class);

public static void main(final String... args) {

// Set up a simple configuration that logs on the console.

logger.trace("Entering application.");

Bar bar = new Bar();

if (!bar.doIt()) {

logger.error("Didn't do it.");

}

logger.trace("Exiting application.");

}

}

package self.samson.bu;

import org.apache.logging.log4j.Logger;

import org.apache.logging.log4j.LogManager;

public class Bar {

static final Logger logger = LogManager.getLogger(Bar.class.getName());

public boolean doIt() {

logger.entry();

logger.error("Did it again!");

return logger.exit(false);

}

}

JVM执行过程

加载主类

链接:验证、预处理和解析(可选地)

初始化:执行初始化器

执行主方法

接下来,结合jls中定义步骤,分析一下JVM执行MyApp中main方法的过程。

加载主类self.samson.bu.MyApp

当JVM首次尝试执行MyApp类的main方法时,会发现该类尚未被加载到内存中,即内存中不存在与MyApp.class相对应的Class对象。此时,JVM会使用类加载器,将MyApp.class加载到内存中。调试发现,此时使用的类加载器为AppClassLoader。如果类加载器无法在classpath中找到MyApp.class,JVM将抛出NoClassDefFoundError。

链接

主类被加载过后,JVM会对加载的类进行链接。链接过程主要包括验证、预处理和解析。

验证。首先,JVM会验证加载的MyApp类是否是格式良好的、是否具有适当的符号表、以及是否遵循Java编程语言和JVM语义规范。

预处理。其次,JVM会为类中的静态域(类变量和常量)分配空间,并把这些空间初始化为默认值。注意,此时不会执行任何源代码,初始化器的执行会在初始化阶段,而不是预处理阶段。除了为MyApp中的静态变量(logger)分配空间外,JVM还需要为一些它内部使用的对象(例如方法表等)分配空间。

解析。最后,检查MyApp对其他类或接口的引用,加载这些类或接口,并检查这些引用是否正确。这一步是可选地,如果没有引用其他类,此阶段可以跳过。这里需要加载org.apache.logging.log4j.Logger。

初始化

初始化阶段会执行类变量初始化器(initializer)和静态初始化器。在MyApp中,只有一个类变量初始化器,即logger = LogManager.getLogger(MyApp.class);。当JVM尝试调用org.apache.logging.log4j.LogManager类的getLogger方法时,它会发现该类尚未加载。JVM会按照之前的进程去加载、链接、初始化LogManager类。LoggerManager包含多个类变量初始化器和一个静态初始化器。如下所示:

// 类变量初始化器

public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";

public static final String ROOT_LOGGER_NAME = "";

private static final Logger LOGGER = StatusLogger.getLogger();

private static final String FQCN = LogManager.class.getName();

private static volatile LoggerContextFactory factory;

// 静态初始化器

static {

PropertiesUtil managerProps = PropertiesUtil.getProperties();

String factoryClassName = managerProps.getStringProperty("log4j2.loggerContextFactory");

if (factoryClassName != null) {

try {

factory = (LoggerContextFactory)LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class);

} catch (ClassNotFoundException var8) {

LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);

} catch (Exception var9) {

LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, var9);

}

}

// more...

}

这些初始化器的执行,会按照文本顺序进行。执行过程还会引起JVM对其他类(例如org.apache.logging.log4j.status.StatusLogger、org.apache.logging.log4j.util.PropertiesUtil等)的加载。过程与上面介绍的MyApp类的加载过程类似,此处就不再过多地叙述。

等所有的依赖都加载并初始化完毕后,MyApp类的初始化才会结束。此外,MyApp的初始化必须在它的直接父类或父接口的初始化之后完成。前面的代码中,MyApp的直接父类只有java.lang.Object,如果它还没被初始化过,会先初始化Object类。

类的初始化过程包括:执行静态初始化器和执行静态变量(类变量)的初始化器。接口的初始化过程包括:执行域(常量)的初始化器。jls中定义了类或接口T初始化发生的时机:

当T为类时,创建T的对象时,会触发T的初始化;

T中定义的静态方法被调用时,会触发T的初始化;

T中定义的静态域被赋值时,会触发T的初始化;

T中定义的非常量域被使用时,会触发T的初始化。

Initialization of an interface does not, of itself, cause initialization of any of its superinterfaces.

接口的初始化本身不会导致任何超接口的初始化。

执行主方法

当MyApp的初始化过程结束后,JVM会再次尝试执行它的main方法。jls中定义了两种可接受的main方法声明方式:

public static void main(String[] args)

public static void main(String... args)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值