浅谈java类加载过程和双亲委派机制

      首先我们需要明确,java中的类只会加载一次,而且java中的类是一种惰性加载,姬用到哪个类,才会去加载哪和个类这个其实很好理解,我们在实际开发项目中引入了很多包,这里面的每个类不一定都能用到,jvm启动运行的时候不可能一次把所有的类都加载一遍。一定是程序运行过程中需要哪个类去加载哪个类。

那么那些场景会导致类的主动加载呢?

1、\color{red}{通过new关键字导致类的初始化},这是最常见的一种类的加载方式,当我们创建一个对象的时候,首都需要看一下这个类是否加载,如果没有加载类,需要先加载类。2、访问类的静态变量,2、访问类的静态变量\color{red}{注意这里是静态变量而不是静态常量。访问类的静态常量不会导致类的初始化}

3、访问类的静态方法

4、对某个类进行反射操作

5、初始化子类会导致父类的初始化

6、启动类,执行main函数所在类导致类的初始化

   这里需要注意的是 构造一个类的数组不会导致类的加载,仅仅是分配了一定的内存空间 比如 A[]a=new A[5];这里不会有A的类加载,另外 A a=null,这种情况也不会有类的加载。

了解了类的加载的触发过程,那我们需要探究一下类的加载过程,类的加载过程可以分为一下几步

1、加载  从磁盘通过IO读取claaa字节码文件,往jvm虚拟机加载

2、验证 校验字节码文件的正确性,是否符合ava规范

3、准备 给类的静态变量分配内存,并且分配默认值,int->0,boolean->false,引用类型->null等

4、解析 将符号引用转变为直接引用(静态链接过程)将静态方法(方法名,变量名)转化为内存地址。静态方法的符号对应地址在类加载期间就可以确定,并且不会改变,所以可以做静态链接

5、初始化 对类的静态变量初始化赋真正的值 执行静态代码块(这里要多说一次,静态代码块就是这个时候执行的,因为类只加载一次,所以静态代码块也只执行一次,小伙伴们面试经常碰到面试题,看一段代码,判断执行结果,而这段代码往往就有静态代码块,其实考察的就是类加载故丛横)

我们知道java是一门面向对象语言,,前面我们了解了类的加载过程,那么对于java来说,谁来完成我们的类的加载这个工作呢,这就是我们下面要说的,类加载器,类加载器其实没有什么神秘的,它就是一个对象,干的就是类加载的活。严格来说,类加载器分为:

1、引导类加载器(bootstrapLoader)负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等

2、扩展类加载器(ExtClassLoader)负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包

3、应用程序类加载器(AppClassLoader)负责加载ClassPath路径下的类包,主要就是加载你自己写的那 些类

4、自定义加载器 开发者自己写的类加载器,可以指定加载路径

下面这个代码输出结果可以很好的体现这几种类加载器

public class Demo1 {

    public static void main(String[] args) {

         System.out.println(String.class.getClassLoader());

         System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());

        System.out.println(Demo1.class.getClassLoader().getClass().getName());

      }

}

运行结果

null 

sun.misc.Launcher$ExtClassLoader

sun.misc.Launcher$AppClassLoader

\color{red}{需要说明的是 String类的加载器是引导类加载器,这个类是c++写的,在jvm中看不到,所以是null}

我们知道了类的加载过程,加载类的双亲委派机制,那么什么是双亲委派机制呢,其实双亲委派机制可以这么描述,加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。概括起来一句化,父加载器先加载,加载不到自己才去加载。下面的图会更详细体现这个过程



这里我们需要明确,类加载器的父类加载器并不是继承关系,比如应用程序类加载器的父加载器是扩展类加载器,但是二者并不是继承关系,而是应用程序类加载器的字段成员属性parent是引导类加载器。下面这个图展示的是从c++创建引导类加载器到初始化扩展类加载器和应用程序类加载器的过程,从Launcher类的源码可以看出,初始化先初始化了扩展类加载器,然后作为参数传入初始化应用程序类加载器当中


下面我们看一下实现双亲委派机制的源码,所有的类加载器都是继承ClassLoader类,该类的loadClass方法就是主要实现了双亲委派机制

protected Class<?> loadClass(String name, boolean resolve)

        throws ClassNotFoundException

    {

        synchronized (getClassLoadingLock(name)) {

            // 先检查类是不是被加载过,保证类只加载一次

            Class<?> c = findLoadedClass(name);

            if (c == null) {

                long t0 = System.nanoTime();

                try {

        // 如果parent存在,由parent加载

                    if (parent != null) {

                        c = parent.loadClass(name, false);

      // 扩展类加载器的parent为null 用引导类加载器加载

                    } 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();

                }

下面我们来探讨一下,为什么java要设计双亲委派机制:

1、避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性

2、沙箱安全机制,防止核心API库被随意篡改,这个这么理解呢,我们知道类的加载是根据全限定包名类名来确定的,也就是说我全限定包名类名相同,jvm便认为是一个类,那么如果我们自己写一个String类,全限定包名类名和jdk源码的包名类名一样,那是不是我们就可以篡改jdkString类的api了呢,通过双亲委派机制,尽管我们可已写这么一个类,但是加载类的时候,应用程序类加载器会先让父加载器扩展类加载器加载,而扩展类加载器会先让引导类加载器加载,引导类加载器加载的是jdk自己的String类,这就导致我们的string类不加载,这就保证了jdk的核心api不会被篡改。

那么是否可以打破双亲委派机制呢

  我们可以通过自定义类加载器拉打破双亲委派机制。核心的是我们继承Classloader这个类,重写loadClass方法便可以实现按照自己意愿去加载类而不用遵循双亲委派机制Tomcat就是个很好的例子,TomCat为什么要打破双亲委派机制

我们思考一下:Tomcat是个web容器, 那么它要解决什么问题:

1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的 不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是 独立的,保证相互隔离。

2. 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程 序,那么要有10份相同的类库加载进虚拟机。

3. web容器也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,应该让容器的 类库和程序的类库隔离开来。

4. web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中 运行,但程序运行后修改jsp已经是司空见惯的事情, web容器需要支持 jsp 修改后不用重启。

再看看我们的问题:Tomcat 如果使用默认的双亲委派类加载机制行不行? 答案是不行的。为什么?

 第一个问题,如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,默认 的类加器是不管你是什么版本的,只在乎你的全限定类名,并且只有一份。 第二个问题,默认的类加载器是能够实现的,因为他的职责就是保证唯一性。 第三个问题和第一个问题一样。 我们再看第四个问题,我们想我们要怎么实现jsp文件的热加载,jsp 文件其实也就是class文 件,那么如果修改了,但类名还是一样,类加载器会直接取方法区中已经存在的,修改后的jsp 是不会重新加载的。那么怎么办呢?我们可以直接卸载掉这jsp文件的类加载器,所以你应该想 到了,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载 器。重新创建类加载器,重新加载jsp文件




©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容