这一篇要说的是类加载机制。
所谓类加载机制也就是Java 虚拟机从磁盘装载 .class 文件或者网络中二进制字节流并且加载Java类的方式或者过程。
具体就是这个图~
image.png
当我们写一个类,并且将其装配到内存中由JVM驱动执行,要说的就是如何去装配。如何由静态的文件结构变为运行时数据结构。
类加载机制就这么几个过程:
加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking)
image.png
加载阶段:
1、图里面的加载就是指从网络或者文件中装载class的过程,也就是先无关对错将内容先装载进来。
这个过程是有三件主要的事儿要做的:
连接阶段
2、验证阶段主要针对class 文件进行验证,是否合规。
主要就是这几种验证过程:文件格式验证(其实大多文件的区分是通过开头几位的魔术来验证的并非扩展名)、元数据验证(语义解析阶段)、字节码验证(语义验证)、符号饮用验证(验证动作是否可正常执行)
3、准备阶段说白了就是真正为类变量在方法区中分配内存并设置类变量初始值的阶段。这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在堆中。
4、解析阶段就是JVM将常量池内的符号引用转换为直接引用的过程。解析动作针对的符号引用主要是有:类、接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限制符。
初始化阶段
5、初始化阶段是类加载过程的最后一步。加载、连接阶段很显然都是JVM主动的在为我们的class作出一些动作,我们Class 里面的代码并没有执行。但是到了初始化阶段就真正开始执行class 中的代码了,执行顺序呢,自上而下根据加载顺序来。这也就是static 代码块执行的来源了。具体来说是由<clinit>( )方法执行初始化的,然后这个方式是JVM可选的,若一个类中并没有类变量也就并不存在什么clinit方法了。当然啦,初始化阶段是并发安全的,这一点就不用赘述了,JVM 自身来保证这一点。利用这一点我们可以在并发中做很多事情的~
注:加载和连接并不是完全同步的,没有绝对的顺序,做完这个然后下一个。而是相对先后的。
动态装载 是Java 很大的一个特性,也是非常好的一个特性。那触发加载Class 的时机是什么时候呢,显然易见就是用着这个类的时候。大约有这么几种情况
很显然的特点,想使用但是它不在的时候就会触发加载。
触发,加载过程都已经说清楚了。
使用不用说,就是说我们Class 中代码执行的过程
最后是卸载阶段。卸载的过程其实就是方法区中废弃类被清扫的过程。
被废弃的类,这个的判断标准之前应该是说过。
1>该类所有的实例都已经被回收
2>加载该方法的ClassLoader已经被回收
3>该类对应的Class对象已无引用,并且无法通过反射访问。
说的差不多了,然后再说一点,双亲委派模型~这是JVM 类加载的一种推荐的模型,而非标准化或者强制化模型。大多数都是按照JVM推荐的方式进行的,比如说tomcat。当然也有非双亲委派的,比如eclipse。
什么叫做双亲委派呢。
双亲其实和我们常用的双亲节点一个意思,就是指的父节点。也就是子节点能完成的加载动作并不会去执行,而是交给他的父亲,如果父亲完成不了再自己去加载。以此类推。
画个经典图看看吧~
image.png
其中绿色框框为JVM默认的类加载器,红色的为tomcat自己的
蓝色箭头为继承方向,绿色箭头为委派方向。
为什么要这么做?
因为不同工程需要共享一些类,封闭一些类。
一个类是由 加载它的类加载器及这个类的全限定名称来标示的,也就是说我们的一个class 文件中的类由不同加载器加载,是不在一个类空间的。
当然啦tomcat中还有针对Jsp这类文件的解析和加载方式。
我觉着这个图我画的特好,再放一遍,差不多就这些~
image.png