JVM详解之类加载器究竟做了什么

JVM就是Java Virtual Machine ,Java 虚拟机

Java之所以能够实现一次编译,多次运行(Write Once,Run anywhere),就是因为JVM,其第一次编译生成了字节码文件(classFile),接下来只需在有JVM环境的任何操作系统上都可以运行

在Java虚拟机中,也就是JVM中,首要的就是类加载器,其首先对字节码文件进行加载,链接,初始化操作。

加载

加载过程

1.通过一个类的权限定名获取定义此类的二进制字节流

2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

3.在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口

注意

ClassLoader 只负责class文件的加载,至于是否可以运行,具体由Execution Engine来决定

不管是什么样的字节码文件,在开头都会有特定的文件标识,在二进制字节流中,就是CA FE BA BE。

加载的类信息存放于内存中的内存空间的方法区中,其中还包括了运行时的常量池信息。

如果装载的时候失败了,就会抛出异常,否则就会进入链接阶段

链接

链接过程

验证(Verify)

验证被加载的类的准确性,保证安全,不被恶意修改

有四种验证方式,文件格式验证,元数据验证,字节码验证,符号引用验证

准备(Prepare)

为类变量分配内存并且设置该类变量的默认初始值,也就是零值,即在准备阶段,类的变量都会是默认值,只有到了初始化阶段(initization),才会赋值

如果用final修饰的static变量,那么在这个阶段,就会显示初始化,因为final在编译的时候就分配了空间和值

解析(Resolve)

将常量池内的符号引用转换为直接引用的过程

解析伴随着初始化执行完之后再执行

在程序中,我们通常会用到一些这样那样的类,而这些类的目标又不太可能包含在小小的字节码文件中,所以,在加载时就会有一些对应的引用,而解析阶段就是将这些符号引用转化为直接引用,

初始化

初始化阶段会执行类构造器方法<clinit>()

这个方法不需要定义,是javac编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并而来,因此,如果没有类变量赋值或静态代码块,那么不会生成这个方法的

构造器方法中指令按语句在源文件中出现的顺序执行

如果这个类有父类,那么JVM会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕

虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁

一个类只会被加载一次

JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)自定义类加载器(User-Defined ClassLoader)

从概念上来讲,自定义加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。

类加载器代码实例

/**
 * ClassLoaderTest 类
 * 操作人:小白
 * 日期:2021/10/11
 * 时间:12:53
 */
public class ClassLoaderTest {
        public static void main(String[] args){

            // 获取系统类加载器
            ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
            System.out.println(systemClassLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2

            // 获取其上层:扩展类加载器
            ClassLoader extClassLoader = systemClassLoader.getParent();
            System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@1b6d3586

            //获取其上层:获取不到引导类加载器
            ClassLoader bootstrapClassLoader = extClassLoader.getParent();
            System.out.println(bootstrapClassLoader); //null

            // 对于用户自定义类来说:默认使用系统类加载器进行加载
            ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
            System.out.println(classLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2

            // String类使用引导类加载器进行加载的。 --> Java 的核心类库都是使用引导类加载器进行加载的。
            ClassLoader classLoader1 = String.class.getClassLoader();
            System.out.println(classLoader1); //null
        }
}

运行结果: 

 


版权声明:本文为Ragel原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。