类加载,就是把class文件装进jvm并做好运行准备的一个过程。
总共要三步,第一步打开jvm,第二步把类放到jvm中,第三步关上jvm。
不开玩笑,正式开始。
先思考:这个过程到底需要做什么?
学习一个知识,如果能通过自己已知的东西来进行比喻或者对比就更好理解,那么我们把类加载类比成:做饭。
jvm是厨房,各种类就是各种食材,食材必须要处理成锅所能接受的状态才能下锅,你不能说食材带着泥巴就下锅、或者找不到食材在那让锅干烧,不合理。
那么这个过程应该是:首先找到所需食材,然后食材处理,最后在厨房按菜谱来制作。
下面细致的说一说这三个步骤。
1、获取食材
顾名思义就是把食材从它所存放的位置处找到并拿到厨房来,比如有些食材在冰箱中,有些在干燥阴凉的地方,有些在柜子里等等,那么谁来找食材呢?当然是人,那么需要几个人来找呢?像饭店的后厨食材非常多,肯定需要多个人去找,如果多个人的话会不会拿到重复的食材呢?怎么解决这个问题呢?
带着上面的问题来看jvm的类加载的第一步,就叫做
1、装载(Load):
1.1找
把class文件从它所在的磁盘存放位置找到,转换为二进制流对象后,放到jvm中。
那么谁来找?有个对象叫ClassLoader,有多种classLoader吗?有(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.3
),不同路径对应了不同的类加载器,甚至你还可以按规范自定义一个类加载器,那么怎么让不同的类加载器不会重复加载同一个类呢?双亲委派机制。(面试点一)这个机制特别像安卓的view事件传递机制,只不过传递顺序是反过来的,view事件传递是先由”父“拿到事件一层一层给”子(具体某个控件比如一个button)“,如果”子“不处理再一层层传回给”父(activity)“;双亲委派机制是高层类加载器收到某一个类的加载请求,会先委派它的”父“加载器去查看其是否也有这个类的加载请求,最终这个请求会传送到最基层的类加载器(Bootstrap),如果这个加载器找不到请求的类,会把请求传递回给他的”子“加载器,子加载器此时才会自己去加载。回到举的例子当中,当一个人A需要拿胡萝卜,他会问下他的领导B有没有需要拿胡萝卜,他领导也会问其领导C,直到问到最高级的领导D,最高级的领导D如果也有需要胡萝卜,那就他拿,如果没需要,再反馈给C,C如果也没需要再反馈给B。 层级关系:
扩展:有没有感觉这样询问效率很差?但是可以保证加载的类的唯一性。假设加载器都是平级,询问时发一个全局广播通知询问,效率会比现在的高吗?广播询问可能有多个loader回复需要加载,还要做区分、去重、优化等策略。
好了, 此时拿到食材是不会重复了,但是可能会出现A需要拿胡萝卜,但是他找不到。。。需要让B去帮他找胡萝卜,这时候就是打破了双亲委派机制(面试点二)
标注的这两个面试点网上都已经解释的很清楚了,这里不细说。
1.2存放
食材找到后拿到厨房(jvm)来,怎么放也是有讲究的,你不能随便乱放,乱放可能会找不到,也可能会导致厨房放不下,那就尴尬了。
对应的就是类信息(常量、变量、方法等等)和类对象等等存放在jvm的划分好的区域位置,依然有据可循,看这里(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5)。
jvm为什么要这么划分区域,这些区域有什么精妙的规则这里就不说了,后面有空再展开聊,也可以先看看我的另一篇文章中的”cpu是如何执行函数调用的“(https://www.jianshu.com/p/f0b966b94b69)。
网友评论