Java类加载机制(类加载器、双亲委派)

类加载的时机
  1. 创建类的实例:new、反射、反序列化、clone;
  2. 访问类中的某个静态变量,或者对静态变量进行赋值;
  3. 主动调用类的静态方法;
  4. ClassForName(“包类名”),得到的class已经初始化完成,ClassLoader.loadClass(“包类名”)得到的class是还没有链接(验证,准备,解析)的;
  5. 完成子类的初始化,也会完成对本类的初始化(接口除外);
  6. 该类是程序引导入口(main入口或者test入口);
类加载过程

  class文件中保存着虚拟机将要执行的指令,当需要某个类的时候,java虚拟机会通过类的全限定名从磁盘中加载class 文件,并创建对应的java.lang.Class对象。将class文件加载到虚拟机的内存,这个过程被称为类的加载。
类加载流程

  1. 加载:ClassLoader通过一个类的完全限定名查找此类字节码文件,并利用字节码文件创建一个class对象,存储在方法区;
  2. 验证:目的在于确保class文件的字节流信息符合当前虚拟机要求,不会危害虚拟机自身的安全,主要包括四种验证:文件格式的验证、元数据的验证、字节码验证、符号引用验证;
  3. 准备:为类变量(static修饰的字段变量)分配内存并且设置该类变量的初始值,在初始化的阶段真正赋值;
  4. 解析:这里主要的任务是把常量池中的符号引用替换成直接引用,下面就是Java字节码文件中的符号引用;
#1 = Methodref          #7.#19         // java/lang/Object."<init>":()V
#2 = Fieldref           #20.#21        // java/lang/System.out:Ljava/io/PrintStream;
#3 = Fieldref           #6.#22         // intron/classtest/ClassTest.TEST:Ljava/lang/String;
#4 = Methodref          #23.#24        // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = String             #25            // test
#6 = Class              #26            // intron/classtest/ClassTest
#7 = Class              #27            // java/lang/Object
  1. 初始化:为类的静态变量赋值,然后执行累的初始化语句(static代码块);
初始化的详细过程:
  1. 如果类还没有被加载和链接,那就先进行加载和链接;
  2. 如果类还存在父类,并且父类还没有初始化,那就先初始化直接父类;
  3. 如果类中存在初始化语句,顺序执行初始化语句;
类加载器

  类加载器是类加载流程的实现者,JDK自带了三个类加载器:Bootstrap ClassLoader(引导类加载器)、Extension ClassLoader(扩展类加载器)、Application ClassLoader(应用类加载器)。

BootStrap ClassLoader:
  1. JVM自带的引导类加载器,由C/C++的语言实现,是C++的对象,在Java中打印该加载器对象为null;
  2. 加载Java的核心类库,$JAVA_HOME中jre/lib/rt.jar、resource.jar或Java程序运行指定的Xbootclasspath选项jar包;
  3. 指定加载java、javax、sun等开头的包名类,不能自定义这些包名;
Extension ClassLoader:
  1. Java语言编写的类加载器:sun.misc.Launcher$ExtClassLoader;
  2. 指定BootStrap ClassLoader为Parent加载器–> getParent()可以获取Bootstrap ClassLoader;
  3. 负责加载Java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/ext或-Djava.ext.dirs指定目录下的jar包(如果我们自定义的class需要交给Ext来加载可以放置在ext的目录下);
Application ClassLoader:
  1. Java语言编写的类加载器:sun.misc.Launcher$AppClassLoader;
  2. 该加载器是Java程序默认的类加载器,Java应用的类都是该加载器加载的;
  3. 指定Extension ClassLoader为parent加载器–> getParent可以获取Extension ClassLoader;
  4. 负责加载环境变量classpath指定的目录,或者java.class.path指定的目录类库;
双亲委派机制:

  如果一个类加载器收到了加载类的请求,它并不会自己先去加载,而是把这个请求委托给父类加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最后到达顶层的引导类加载器。如果父加载器能够完成类的加载任务,就会成功返回,倘若父类加载器无法完成任务,子类加载器才会尝试自己去加载,这就是双亲委派模式。
双亲委派机制

双亲委派机制核心代码:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

  采用双亲委派模式的好处就是Java类随着它的类加载器一起具备一种带有优先级的层次关系,通过这种层级关系可以避免类的重复加载,当父类加载器已经加载了该类,子类加载器就不需要再加载一次。其次是考虑到安全因素,Java核心API中定义类型不会被随意替换,如果我们人为定义java.lang包并在包内添加一些自定义类,这样做是不允许的,因为java.lang是核心的API包,需要访问权限,强制加载将会报出如下异常。

java.lang.SecurityException:Prohibited package name: java.lang
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java类加载器采用双亲委派机制,这是Java安全模型的重要组成部分。这种机制保证Java核心API不会被随意篡改,同时也保证了Java程序的稳定性和安全性。 双亲委派机制的基本原则是:当一个类加载器接收到类加载请求时,它首先将该请求委派给它的父类加载器去完成,直到最顶层的父类加载器。只有在父类加载器无法完成类加载任务时,才由子类加载器自行加载。这种机制确保了Java核心API的安全性,因为只有Bootstrap ClassLoader能够加载Java核心API,其他类加载器都无法篡改这些类。 在双亲委派机制中,每个类加载器都有一个父类加载器。如果一个类加载器需要加载某个类,它会先委托给它的父类加载器去加载。如果父类加载器无法加载该类,才会由该类加载器自己去加载。这样一来,如果一个类已经被加载了,那么其类加载器的父类加载器肯定已经加载了该类,因此不会重复加载,也就避免了类的重复加载。 双亲委派机制的实现是通过ClassLoader类的loadClass()方法实现的。这个方法首先检查是否已经加载了该类,如果已经加载了就直接返回,否则就委托给父类加载器去加载。如果父类加载器无法加载该类,就调用findClass()方法自己加载。这样一来,每个类加载器都只需要实现自己的findClass()方法,而loadClass()方法则由ClassLoader类统一实现,从而实现了双亲委派机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值