我们知道一个 java 类想要被执行就必须被加载到内存中,而加载的过程呢有大体可以分为 加载、连接、初始化、使用、卸载,五部分,下面,我们就一起看一下各个部分 JVM 都做了什么。
首先,我们将这段代码编程成 class 文件,然后运行。
/**
* Created by shengjk1 on 2020/8/30.
*/
public class Test {
public static int b=7;
public static void main(String[] args) {
System.out.println("b = " + b);
System.out.println("执行完毕");
}
}
其实就是从非内存的位置到内存中的一个过程。主要做三件事
这个过程呢,也主要是依靠类加载器来完成的
获取 classLoader 是 null 就是 Bootstrap ClassLoader
扩展类加载器
Java 语言编写,派生于ClassLoader 类。
系统属性 java.ext.dir 或者 JDK安装目录 jre/lib/ext
Java 语言编写,派生于 ClassLoader 类,父类为 扩展类加载器
负责加载环境变量 classpath 或系统属性 java.class.path 下的类库
该类加载器是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载
至此为止,我们的 Test.class 就加载到了 JVM 中,而 JVM 呢为了自身的安全会进行一系列的校验。校验的过程叫做 验证
目的在于确保 Class 文件的字节流中包含信息符合当前虚拟机的要求,保证被加载类的正确性,不危害虚拟机自身的安全。
文件格式验证、元数据验证、字节码验证、符号引用验证
验证完毕之后,就开始为执行 Test.class 做准备了
为类变量分配内存并设置类变量初始值的阶段。准备了类的变量初始值之后,虚拟机会把该类的虚方法表也一同初始化完毕
所以此时会为类变量 b 分配内存空间然后设置初始值 b=0。而在 JDK8 以后,类变量会被分配在 Java 堆中
准备阶段完成了,但我们的 Test 类要想被执行还差最重要的一步:解析
将常量池中符号引用转换为直接引用. ( 有了直接引用,那引用的目标必定已经在虚拟机的内存中存在了 )
什么是直接引用呢,其实就是真实的内存地址,就是把我们 b 的引用设置为内存地址,后面调用的时候就可以直接访问内存地址了,不需要再进行解析。
在连接阶段完成之后,就开始正式执行 main 方法了,由于 main 方法是 public static 的,所以 JVM 会判断 Test 类是否已经初始化了,如果没有则对其进行初始化
一般在下面这 6 中情况下会进行初始化
而我们的 main 方法满足 1 条 invokestatic
初始化过程中会对所有的类变量进行赋值操作,会执行静态代码块,此时我们 b 就等于 7 了
至此为止,才真正的开始执行 main 方法。会通过系统调用将 b 的值打印到控制台
Test 类运行完毕之后,为防止其继续占用内存,会将其卸载,也就是从内存中将其删除。
其实,基本上每个阶段都是交叉执行的,而验证、准备、解析,有统称为连接
技术创作101训练营
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。