在学习反射那一章节时想到自己之前学过的知识,故整理一番,希望能提供一点帮助,水平有限,如若有误欢迎指正。
Java提供了两种类的装载方式。一是预先加载,二是按需加载。因为可以对类进行按需加载,所以程序在启动时不需要把所有的类都装载到JVM中,大部分类都被延迟到使用时才动态加载。
1.预先加载
这里有Java基础类的加载和含main()函数类的加载。类加载器见文末补充。
Java基础类的加载流程:启动程序到jdk目录下找到并载入jvm.dll启动虚拟机初始化参数创建BootStrap Loader对象。
BootStrap Loader对象称为启动类装载器,它会在虚拟机启动时一次性加载JJVM的基础类。
含main函数的类加载:BootStrap Loader另一项工作就是负责装载定义在sun.misc命名空间下的Launcher类。Launcher类有两个内部类,ExcClassLoader和AppClassLoader,其中,ExtClassLoader的父加载器被设置为null,表示它的父加载器为BootStarp Loader,即它由BootStrap Loader直接装载,而AppClassLoader的父加载器为ExtClassLoader,含有main函数入口的类由AppClassLoader在程序启动时加载。
2.按需加载
即运行时动态装载,当需要使用这个类时,JVM才会去动态装载它。
1.装载条件:当一个类的静态成员被第一次引用时,JVM就会去装载它,包括【静态方法-静态属性-构造方法】
需要特别注意的是:
①当访问静态常量属性时,JVM加载类不会进行类的初始化工作。
②虽然构造方法没有被显式地声明为静态方法,但它仍是类的静态成员特例,因此,当使用new关键字来构造对象时,会被当成类静态成员的引用,从而触发JVM装载该类。
2.按需装载流程:
当需要使用某个类时,JVM首先会去检查该类的Class对象是否存在已经加载,如果没有,则执行以下步骤。
1)加载:查找并导入类的字节码,根据这些字节码创建Class对象
2)链接:其中分为三步
①校验:检查导入的字节码的完整性,正确性、安全性。
②准备:为静态域分配存储空间
③解析:将符号引用转折为直接引用(非必需)
3)初始化:初始化静态变量并执行静态域代码,上文提到,当访问类的常量静态属性时,类的初始化工作不会进行,真正的初始化工作会被延迟到对静态方法或非常量静态属性的首次访问时。
补充:类加载器
每当创建一个Java类的实例时,必须先将该类加载到内存中。JVM使用类加载器来加载类。Java加载器在Java核心类库和CLASSPATH环境下面的所有类中查找类。如果找不到,会抛出常见的 java.lang.ClassNotFoundException异常。
目前JVM使用3种类加载器:bootstrap类加载器、extension类加载器、system类加载器。三者是父子关系,其中,bootstrap类加载器在顶端,而system类加载器在结构的最底层。
- bootstrap类加载器用于引导JVM,一旦调用java.exe程序,bootstrap类加载器就会开始工作,然后加载JVM所需要的类到函数中,其次,它还负责加载所有的Java核心类,如java.lang和java.io包等,另外,bootstrap还会根据JVM和操作系统来查找如rt.jar,i18n.jar等核心类库。
- extension类加载器负责加载标准扩展目录下面的类,JVM的标准扩展目录是/jdk/jre/lib/ext。
- system加载器是默认加载器,它负责在环境变量CLASSPATH目录下查找相应的类,这也就是我们为什么要配置好jdk环境变量的原因之一。
PS:JVM如何确定使用哪个类加载器呢?其实是通过委派模型来实现的,每次类需要加载,system类加载器首先被调用,但是它并不会马上加载类。而是将任务委派给它的父类extension类加载器。而extension类加载器也把任务委派给它的父类加载器bootstrap加载器。这也就是为什么bootstrap类加载器总是首先加载类的原因。如果父类加载器不能找到所需的类完成加载,这时才由子类尝试加载。如果连system类都加载失败,则抛出java.lang.ClassNotFoundException异常。
做这个笔记一是想自己整理一下这方面的知识,方便以后更深入学习Java虚拟机。其次也希望能整理出来帮助到他人。