ClassLoader机制

1.ClassLoader概念
ClassLoader是用来动态的加载class文件到虚拟机,并转换成java.lang.class类的一个实例,并通过实例的newInstance()方法创建出该类的一个实例。除此之外,ClassLoader还负责加载java应用所需的资源,如图片文件和配置文件。
ClassLoader类其实是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。ClassLoader类使用委托模型来搜索类和资源。每个 ClassLoader实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。

2.简介ClassLoader的流程
首先JVM启动的时候,并非一次性加载所有的class文件,而是通过(Bootstrap classLoader)启动类加载器来加载JVM自身工作需要的类,如java.lang.,java.util.等等;这些类位于&JAVA_HOME/jre/lib/rt.jar。启动类加载器不继承自ClassLoader,是在底层C++编写,已经嵌入到了JVM内核当中,当JVM启动后,启动类加载器也随之启动,负责加载核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器
ExtClassLoader:扩展的class loader,加载位于JAVA_HOME/jre/lib/ext目录下的扩展jar。 AppClassLoader:系统class loader,父类是ExtClassLoader,加载CLASSPATH下的目录和jar;它负责加载应用程序主函数类。
如果要实现自己的类加载器,不管是实现抽象列ClassLoader,还是继承URLClassLoader类,它的父加载器都是AppClassLoader,因为不管调用哪个父类加载器,创建的对象都必须最终调用getSystemClassLoader()作为父加载器,getSystemClassLoader()方法获取到的正是AppClassLoader。

注意:Bootstrap classLoader并不属于JVM的等级层次,它不遵守ClassLoader的加载规则,Bootstrap classLoader并没有子类

image.png

3.ClassLoader的加载
JVM加载class文件到内存有两种方式:
隐式加载,不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存,例如:当类中继承或者引用某个类时,JVM在解析当前这个类不在内存中时,就会自动将这些类加载到内存中。显示加载,在代码中通过ClassLoader类来加载一个类,例如调用this.getClass.getClassLoader().loadClass()或者Class.forName()。
加载过程:
找到.class文件并把这个文件加载到内存中;字节码验证,Class类数据结构分析,内存分配和符号表的链接;类中静态属性和初始化赋值以及静态代码块的执行。

4.ClassLoader双亲委托模式进行类加载
每一个自定义的ClassLoader都必须继承ClassLoader这个抽象类,父类中有一个getParent()方法,用来返回当前ClassLoader的parent(不是指父类,而是实例化该ClassLoader时制定的一个ClassLoader),如果为null,那默认制定启动类加载器(bootstrap classloader)。这个parent有什么用呢?
我们写自定义的MyClassLoader加载java.lang.String,那么String这个类并不是被MyClassLoader加载,是被bootstrap classLoader进行加载的。这就是双亲委托模式,任何一个自定义的ClassLoader加载一个类之前,它都先委托它的父亲ClassLoader进行加载,如果为null,这个parent默认是bootstrap classLoader,上面的例子结果,最终委托给bootstrap classloader,返回String的Class。
看下面一段源码:

protected Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
        synchronized(this.getClassLoadingLock(var1)) {
            Class var4 = this.findLoadedClass(var1);
            if(var4 == null) {
                long var5 = System.nanoTime();

                try {
                    if(this.parent != null) {
                        var4 = this.parent.loadClass(var1, false);
                    } else {
                        var4 = this.findBootstrapClassOrNull(var1);
                    }
                } catch (ClassNotFoundException var10) {
                    ;
                }

                if(var4 == null) {
                    long var7 = System.nanoTime();
                    var4 = this.findClass(var1);
                    PerfCounter.getParentDelegationTime().addTime(var7 - var5);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(var7);
                    PerfCounter.getFindClasses().increment();
                }
            }

            if(var2) {
                this.resolveClass(var4);
            }

            return var4;
        }
    }

首先传参数:这个方法第一个参数 类名称 name,第二个参数是 是否连接该类 resolve,方法内部的代码显示出一个类加载的大概过程,我们要实现一个自定义的类的时候,只需实现findClass方法即可。
为什么要使用这种双亲模式呢? 第一避免重复加载类,如果父亲已经加载这个类,就没必要子ClassLoader再加载一次;第二出于安全因素,可以试想,JVM启动的时候会加载核心库api,这个时候我们可以随时自定义String来动态替换库中的String了!!!双亲委托模式会检测String已经加载了,而我们自定义的ClassLoader类是不会加载的。

5.Class类
JVM中每个被ClassLoader加载的Class文件,最终都以Class类的实例被我们引用,我们可以把Class类当成是普通类的一个模板,JVM根据这个模板生成对应的实例。Class里面有个静态方法forname

public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        if (loader == null) {
            loader = BootClassLoader.getInstance();
        }
        Class<?> result;
        try {
            result = classForName(name, initialize, loader);
        } catch (ClassNotFoundException e) {
            Throwable cause = e.getCause();
            if (cause instanceof LinkageError) {
                throw (LinkageError) cause;
            }
            throw e;
        }
        return result;
    }                    

这个方法和ClassLoader的loadClass目的一样,都是用来加载class的,但是两者作用有所区别。这里还得重复上面的一个知识点:在JVM加载类额时候,需要三个步骤,装载、连接、初始化。装载就是找到相应的Class文件,读入JVM;初始化就不很简单了(都知道一个类的初始化顺序过程);主要讲一下连接。连接分三步,第一步是验证class是否符合规格,第二步是准备,就是为类变量分配内存同时设置默认值,第三步就是解释,而这一步是可选的。这里看一下ClassLoader的两个加载Class的方法

public Class<?> loadClass(String var1) throws ClassNotFoundException {
        return this.loadClass(var1, false);
    }

protected Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException

是否解释就是根据上边只有两个参数的loadClass方法的第二个参数来设置的,第一个loadClass其实默认调用的是两个参数的loadClass,第二个参数默认false,所以在这里可以看出loadClass加载类实际上就是加载的时候并不对该类做解释,因此也不会初始化该类。而Class类的forName方法相反,使用这个方法加载的时候就会将Class进行解释和初始化。再看一下这个forName方法的参数

public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException

第一个类名称,第二个是否初始化,第三个类加载器,明白了吧!!!
举个例子: JDBC DRIVER的加载,我们在加载JDBC驱动是通过DriverManager,必须在这个类中注册,如果驱动类没有被初始化是不能被注册的,因此必须使用Class的forName方法,不能用ClassLoader的 loadClass方法!!!over。

总结:我们可以用ClassLoader自定义类加载器,定制我们所需要的加载方式,例如从网络加载(和热更新很想,不过热更新是加载Dex文件的),从其他格式的文件加载等等,都是通过ClassLoader,当然ClassLoader还有很多知识。。。待更新吧!

有新的想法请@我哦,我的邮箱是 liugstick@163.com !!!

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

推荐阅读更多精彩内容