栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

(一) 类加载过程详解 (类加载机制 第一篇)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

(一) 类加载过程详解 (类加载机制 第一篇)

当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把主类加载到 JVM。
package com.shendu;

public class JvmTest01 {
    public static final int initData = 666; 

    public int compute() { 
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;
    }

    public static void main(String[] args) {
        new JvmTest01().compute();
    }
}

如上面的代码:当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把主类加载到 JVM。

通过Java命令执行代码的大体流程如下:

以上就是整个从jvm到java中main程序的执行全部过程,main是所有程序的入口。

整个类加载如图所示

加载

加载,是指Java虚拟机查找字节流(查找.class文件),并且根据字节流创建java.lang.Class对象的过程。这个过程,将类的.class文件中的二进制数据读入内存,放在运行时区域的方法区内。然后在堆中创建java.lang.Class对象,用来封装类在方法区的数据结构。

类加载阶段:

(1)Java虚拟机将.class文件读入内存,并为之创建一个Class对象。

(2)任何类被使用时系统都会为其创建一个且仅有一个Class对象。

(3)这个Class对象描述了这个类创建出来的对象的所有信息,比如有哪些构造方法,都有哪些成员方法,都有哪些成员变量等。

验证
验证阶段作用是保证Class文件的字节流包含的信息符合JVM规范,不会给JVM造成危害。如果验证失败,就会抛出一个java.lang.VerifyError异常或其子类异常。验证过程分为四个阶段:

文件格式验证:验证字节流文件是否符合Class文件格式的规范,并且能被当前虚拟机正确的处理。

元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范要求

字节码验证:主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。

符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。

准备
准备阶段为变量分配内存并设置类变量的初始化。在这个阶段分配的仅为类的变量(static修饰的变量),而不包括类的实例变量。对已非final的变量,JVM会将其设置成“零值”,而不是其赋值语句的值:pirvate static int size = 12;。那么在这个阶段,size的值为0,而不是12。但final修饰的类变量将会赋值成真实的值。

解析
解析过程是将常量池内的符号引用替换成直接引用。主要包括四种类型引用的解析。类或接口的解析、字段解析、方法解析、接口方法解析。

初始化

初始化,则是为标记为常量值的字段赋值的过程。换句话说,只对static修饰的变量或语句块进行初始化。

如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。

如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。


类加载器和双亲委派机制

上面的类加载过程主要是通过类加载器来实现的,Java里有如下几种类加载器

  • 引导类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等
  • 扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包
  • 应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写的那 些类 自定义加载器:负责加载用户自定义路径下的类包
双亲委派机制

一个类只有被第一次主动使用时,才会被java虚拟机加载
主动使用的情况(6种)
1、创建类的实例
2、访问类的静态变量
3、调用类的静态方法
4、反射加载
5、初始化一个类的子类
6、java虚拟机启动时被标记为启动类的类

定义了几个类加载器。

  • AppClassLoader 系统类加载器 负责的目录如下:

    • %JAVA_HOME%/jre/lib
    • -Xbootclasspath 参数指定的目录
    • 系统属性sun.boot.class.path
  • ExtClassLoader 扩展类加载器 负责的目录如下:

    • %JAVA_HOME%/jre/lib/ext
    • 系统属性java.ext.dirs指定的类库
  • Bootstrap classLoader 启动类加载器 负责的目录如下:

    • 环境变量 classpath
    • -cp
    • 系统属性java.class.path

各个加载器的加载路径的验证

​ AppClassLoader 加载路径地址

        String appProperty = System.getProperty("java.class.path");
        for (String s : appProperty.split(";")) {
            System.out.println(s);
        }

打印为:

C:Program FilesJavajdk1.8.0_281jrelibcharsets.jar
C:Program FilesJavajdk1.8.0_281jrelibdeploy.jar
C:Program FilesJavajdk1.8.0_281jrelibextaccess-bridge-64.jar
C:Program FilesJavajdk1.8.0_281jrelibextcldrdata.jar
C:Program FilesJavajdk1.8.0_281jrelibextdnsns.jar
C:Program FilesJavajdk1.8.0_281jrelibextjaccess.jar
C:Program FilesJavajdk1.8.0_281jrelibextjfxrt.jar
C:Program FilesJavajdk1.8.0_281jrelibextlocaledata.jar
C:Program FilesJavajdk1.8.0_281jrelibextnashorn.jar
C:Program FilesJavajdk1.8.0_281jrelibextsunec.jar
C:Program FilesJavajdk1.8.0_281jrelibextsunjce_provider.jar
C:Program FilesJavajdk1.8.0_281jrelibextsunmscapi.jar
C:Program FilesJavajdk1.8.0_281jrelibextsunpkcs11.jar
C:Program FilesJavajdk1.8.0_281jrelibextzipfs.jar
C:Program FilesJavajdk1.8.0_281jrelibjavaws.jar
C:Program FilesJavajdk1.8.0_281jrelibjce.jar
C:Program FilesJavajdk1.8.0_281jrelibjfr.jar
C:Program FilesJavajdk1.8.0_281jrelibjfxswt.jar
C:Program FilesJavajdk1.8.0_281jrelibjsse.jar
C:Program FilesJavajdk1.8.0_281jrelibmanagement-agent.jar
C:Program FilesJavajdk1.8.0_281jrelibplugin.jar
C:Program FilesJavajdk1.8.0_281jrelibresources.jar
C:Program FilesJavajdk1.8.0_281jrelibrt.jar
D:tcl_ouyangdemo_codingjvmclassloadtargetclasses
D:tcl_ouyangdev_softIntelliJ IDEA 2019.1.4libidea_rt.jar

虽然打印了这么多的路径,但是其他的jar已经被它的父加载器给加载过了,根据双亲委托机制,其实AppClassLoader只是加载项目中的classpath路径下的类。

ExtClassLoader 加载路径

String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")) {
System.out.println(path);
}

打印

C:Program FilesJavajdk1.8.0_281jrelibext
C:WindowsSunJavalibext

Bootstrap classLoader加载路径

 URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (URL url : urLs) {
            System.out.println(url.toExternalForm());
        }

打印

file:/C:/Program%20Files/Java/jdk1.8.0_281/jre/lib/resources.jar
file:/C:/Program%20Files/Java/jdk1.8.0_281/jre/lib/rt.jar
file:/C:/Program%20Files/Java/jdk1.8.0_281/jre/lib/sunrsasign.jar
file:/C:/Program%20Files/Java/jdk1.8.0_281/jre/lib/jsse.jar
file:/C:/Program%20Files/Java/jdk1.8.0_281/jre/lib/jce.jar
file:/C:/Program%20Files/Java/jdk1.8.0_281/jre/lib/charsets.jar
file:/C:/Program%20Files/Java/jdk1.8.0_281/jre/lib/jfr.jar
file:/C:/Program%20Files/Java/jdk1.8.0_281/jre/classes

我们在来验证下,各个加载器之间的关系

        System.out.println(JvmTest02.class.getClassLoader());
        System.out.println(JvmTest02.class.getClassLoader().getParent());
        System.out.println(JvmTest02.class.getClassLoader().getParent().getParent());

打印:

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null

我们可以得出一个结论:AppClassLoader的父加载器是ExtClassLoader,然后我们的引导类加载器是不对外开放的,因为它是C++编写的,它不是一个类


JVM类加载器是有亲子层级结构的,如下图

这里类加载其实就有一个双亲委派机制,加载某个类时会先委托父加载器寻找目标类,找不到再 委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的 类加载路径中查找并载入目标类。 比如我们的Math类,最先会找应用程序类加载器加载,应用程序类加载器会先委托扩展类加载 器加载,扩展类加载器再委托引导类加载器,顶层引导类加载器在自己的类加载路径里找了半天 没找到Math类,则向下退回加载Math类的请求,扩展类加载器收到回复就自己加载,在自己的 类加载路径里找了半天也没找到Math类,又向下退回Math类的加载请求给应用程序类加载器, 应用程序类加载器于是在自己的类加载路径里找Math类,结果找到了就自己加载了。。 双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载

为什么要设计双亲委派机制?

  • 沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心 API库被随意篡改 避免类的重复加载:

  • 当父亲已经加载了该类时,就没有必要子ClassLoader再加载一 次,保证被加载类的唯一性

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/424398.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号