本文来自http://blog.csdn.net/liuxian13183/ ,援用必须注明出处!
前两节我们探讨了Java类内存模块,文件结构,和Jvm的回收机制,今天我们再来探讨1下它的文件加载机制,都知道Jvm要加载的是2进制流,可以是.class文件情势,也能够是其他情势,总之依照它加载的标准来设计就不会有太大问题,以下主要就机制和标准两个问题分析1番
首先来讲Java类文件的加载机制,跟变量的加载机制类似,它先把Class文件加载入内存,再对数据进行验证、解析和初始化,终究构成虚拟机可以直接使用的Java类型。由于Java是采取JIT机制,所以加载时会比较慢,但优点也明显,具有高度灵活性,支持动态加载和动态连接。接下来就讲讲类的加载进程:
1个类加载的基本进程是依照下面的顺序来,但也有不严格依照这个顺序来的,也有打乱顺序来的,如动态加载就得先初始化再解析。
1、加载- 由虚拟机自行决定,但也有由于下面的阶段要履行而履行上面阶段的情况。这时候虚拟机会做3件事,第1、通过全限定名读取文件的2进制流,第2、把文件里的静态方法和变量放到方法区中,第3、生成1个对象放入堆中,作为访问入口。注意第1条,仅是读取2进制流,没说具体从甚么文件中读,也没说从哪里读,所以造就Java很强的扩大性,可以从Jar、Zip中,也能够从网络层、数据库层等 。主要是对象和方法区的声明。
2、验证 确保2进制流符合虚拟机的要求,不符合会报VerifyError。 第1、文件格式验证,是不是有魔数,是不是符合Java文件的要求,详见:Java高级之类结构的认识;第2、元数据验证,是不是符合Java代码规范,如abstract类是不是直接被实例化,普通类有没有间接或直接父类Object等;第3、字节码验证,对数据流和控制流进行分析,保证不会做出危害虚拟机的行动,如是不是调用不存在的指令,是不是把父类赋值给子类,是不是把对象赋值给1个非此类型的对象等;第4、符号援用验证,主要是类、变量、方法描写是不是能找的到,如全限定名是不是能找到该文件,是不是具有可访问性等。主要对内部结构的判定
3、准备 为类变量赋初值,通常为0值如静态变量,而不会为实例变量赋值。
4、解析 将常量池中的符号援用转化为直接援用的进程。这里说的符号援用指变量类型,直接援用指可以直接定位到对象的句柄。类、方法、字段、接口解析,根据全限定名取得相干对象,拿到它的类型,若无对所在类访问权会抛出IllegalAccessError,无字段NoSuchFieldError,无方法NoSuchMethodError,是类不是接口会抛出IncompatibleClassChangeError
5、初始化 根据程序要求加载类和必要的资源。有且唯一4种情况,需要主动初始化后才能履行接下来的操作 ,所以要先履行上面的4步。第1、有new或static关键字的类,new生成对象,static静态加载,这两个很明显要履行初始化了;第2、使用类有父类,这没办法了;第3、反射类里的方法,那肯定要初始化了对不对;第4、履行的主类,用main方法的类。其他被动初始化的情况不需要斟酌。
小例子:
这个时候加载类构造器<clinit>,会初始化类中所有变量,固然父类先于子类初始化
6、使用 加载完以后,该怎样样调用怎样样调用,绘图啊,计算啊等等
7、卸载 类不再被调用
两个类是不是相等,主要在于第1使用同1个加载器加载,第2全限定名地址1致
为何要提出上面的问题呢?接下来要讲讲虚拟机的1个加载机制。
在虚拟机的角度来看,有两种类加载器,1种叫系统加载器(Bootstrap ClassLoader),1种叫自定义加载器(extends ClassLoader),这类呢又分为两个,1种叫利用加载器,1种叫扩大类加载器,1般默许为前者;而我们的利用程序加载主要由上面3个加载器相互配合完成的。3者的关系如Application-->Extension-->Bootsrap,双亲委派机制是指两两以组合的方式,子加载器先去调用父加载器的方法,没找到目标对象再去用子加载器
伪代码以下:
loadClass(String name,boolean resolve){
Class c=findLoadedClass()
if(c==null){
try{
if(parent !=null)
c=parent.loadClass(name,false);
else
c=findBootstrapClassOrNull(name);
}catch(ClassNotFoundException e){ }
}
if(c==null)
c=findClass(name);
}
Java提倡我们去把自己调用类的逻辑写在findClass里,这样有助于双亲委派机制的正常使用。
破坏1、重写loadClass
破坏2、使用线程上下文加载器去让父加载器去调用子加载器的方法
破坏3、热加载 现在经常使用的做法是自定义类加载器并将原bug模块覆盖-OSGI
但由于自定义加载器之间的规则如果混乱,出现同时相互援用的问题,那末会终究找不到类,而出现线程死锁和内存泄漏的问题。
下1节我们会讨论1下线程并发的问题。