JVM之---类加载过程

来源:互联网 发布:vscode 开发go 编辑:程序博客网 时间:2024/06/11 19:32

1.类加载时机

Class文件存储的是字节码,对编译的语言并没有特定的要求,只要符合Class文件格式规范即可,所以诞生了许多以JVM为基础的编程语言,如Scala,Jython,Groovy等。

类从被JVM加载到卸载出内存为止生命周期包括七个阶段:

其中加载,验证,准备,初始化,卸载是按部就班的,解析则不一定,有时候在初始化之后。

有两个简单的例子:

例子1:

public class ConstClass {        static {        System.out.println("ConstClass init");    }        public static int a = 123;}
public class SubClass extends ConstClass{    static {        System.out.println("SubClass Init");    }}
public class ConfTest {    public static void main(String[] args) {        System.out.println(SubClass.a);    }}
输出为:
ConstClass init
123
对于静态字段,只有直接定义这个字段的类才会被初始化,通过子类来引用父类中的静态字段不会触发子类初始化
例子2:

public class ConstClass {        static {        System.out.println("ConstClass init");    }        public static final String HELLOWORLD = "Hello World";    public static int a = 123;}
public class ConfTest {    public static void main(String[] args) {        System.out.println(ConstClass.HELLOWORLD);        System.out.println(ConstClass.a);    }}
输出为:
Hello World
ConstClass init
123
第一步输出Hello World,原因是因为此时有final关键字,JVM在初始化时将final内嵌入ConfTest类的方法区,此时调用的是ConfTest中的方法区常量。第二步输出ConstClass init是由于此时调用了ConstClass的静态成员变量a,初始化将ConstClass的静态成员变量存储到方法区中


2.类加载过程
2.1 加载
(1)加载完成后,虚拟机外部的二进制字节流按照JVM需要的格式存储在方法区中
(2)在Java堆中实例化一个Class类对象,作为程序访问方法区中这些类型数据的外部接口
2.2 验证
确保Class文件的字节流符合当前JVM的要求,且不会危害JVM的安全
2.3 准备
正式为类变量分配内存,并设置类变量初始值
public static int value = 123
此阶段过后value的值为0,而非123
2.4 解析
将常量池的符号引用替换为直接引用


3.类加载器
类加载阶段给虚拟机一个类的二进制全限定流,让程序自己决定用哪个类,在JVM外部实现
分为两种:
(1)BootStrap ClassLoader
用C++实现,虚拟机的一部分
(2)其他类加载器
Java语言实现,全部集成自java.lang.ClassLoader类
具体来看,分为三种:
a.启动类加载器(BootStrap ClassLoader)
负责将存放在<JAVA_HOME>\lib下的类库加载到虚拟机内存中,无法被Java类直接使用
b.扩展类加载器(Extension ClassLoader)
由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中的所有类库
c.应用程序类加载器(Application ClassLoader)

由sun.misc.Launcher$AppClassLoader实现,负责加载用户路径上锁指定的类库,默认类加载器


一般加载顺序是自定义加载器(如有)在接受到类加载请求后,会将请求委派到父类加载器(Application ClassLoader)中去,在传到Ext类加载器,最终都传到顶层BootStrap类加载器