第一部分
概述:Class文件中描述的各种信息都需要加载到虚拟机中才能运行和使用。
但是具体如何加载到虚拟机中?
Class文件加载到虚拟机中之后会发生什么事情?
虚拟机class文件加载到内存中,并且对class文件中的数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用 的java类型,这就是虚拟机的类加载机制。
类加载的时机:
类的整个声明周期从加载到内存——>卸载出内存为止,共经历:加载,验证,准备,解析,初始化,使用,卸载。
其中:验证,准备,解析统称为连接。
其中:加载,验证,准备,初始化,卸载顺序是固定的。
没有规定类加载的约束,但是有5种情况必须进行初始化。
1.使用new,getstatic,putstatic等关键字的时候。最常见的是实例化一个对象,读取或者设置一个类静态方法的时候, 以及调用一个类静态方法的时候。
2.使用反射的时候,类还没有初始化。
3.当初始化子类的时候,其父类还没有被初始化。
4.启动虚拟机的时候,用户需要指定一个主类,虚拟机会先初始化这个类。
5.JDK1.7中,句柄所对应的类没有进行过初始化,则需要先出发其初始化。
类加载的过程:
加载:加载和类加载是两个概念。
1.通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个类静存储结构,转变为方法区运行时数据结构。
3.在内存中生成这个类的java.lang.class对象,作为在方法去访问这个类的各种数据的接口。
验证:保证class文件的字节楼中包含的信息满足虚拟机要求,不会危害虚拟机。
1.文件格式验证,这个阶段是基于二进制字节流进行的,通过这个阶段之后字节流才能进入内存中的方法区进行存储。
2.元数据验证,针对的是字节码,验证是否满足java语言规范。
3.字节码验证,保证类的方法是合法的,符合逻辑的,在运行的时候是不会危害到虚拟机安全的。
4.符号引用验证,最后一个验证阶段发生在解析阶段中,是将符号转化为直接应用的时候。符号应用是可以看作是对类自身以 的信息进行匹配检索 。
准备:准备阶段是正式为类变量分配内存并设置初始值的阶段,这些变量所使用的内存都将是在方法区中进行的。这里分配的变 量仅仅包括类变量,被static修饰的部分。
解析:解析阶段是虚拟机将常量池中的符号引用转换为直接引用的过程。
第二部分
类加载器:通过全限定名来获取描述此类的二进制字节流的过程中,用到的代码块称之为“类加载器”。
双亲委派模型:
从Java虚拟机的角度来讲,类加载器分为启动加载器和其他的类加载器。启动加载器是虚拟机自身的一部分,由C++实现。 其他类 加载器由java实现,独立于虚拟机外部。
从java开发人员的角度分为三种加载器:
启动加载器,扩展类加载器,应用程式类加载器:一般是默认的类加载器。
类加载器之间的层次关系:其中除了启动类加载器外,其他的类加载器都有自己的父类加载器,而其是通过组合来实现复用代码的。
启动类加载器
扩展类加载器
应用程序加载器
自定义加载器
工作过程:先检测是否加载,如果没有加载则父类加载器加载,如果父加载器为空则默认使用启动类加载器,如果父加载器失败,就抛出异常,然后调用自己的findClass()方法进行加载。
破坏双亲委派模型:
第一次被破坏:在双亲模型之前,人们集成java.lang.classloader唯一目的就是区复习loadclass(),因为虚拟机在加载loadclassinternal()方法的时候,唯一的逻辑就是区调用loadclass()。而现在已经不提倡覆盖loadclass了,而应该把类加载逻辑写到findclass()中,因为在loadclass()逻辑里面,如果父类加载失败就会调用findclass().
第二次破坏:双清委派模型在很好的解决了基础类的统一问题,越是基础的类由越网上的类加载器加载,而基础类之所以被称之为基础类就是因为他是被用户经常调用的api。但是有一个问题是当基础类要调用用户的类该怎么办。因此提出了线程上下文类加载器,也就是父加载器请求子类加载器去完成类加载的动作,这个双亲加载是相违背的。
第三次破坏:第三次破坏是由于人们追求程序动态性而导致的,比如,模块热部署,代码热替换。新提出的OSGi用于模块热部署则是其自定义的类加载器机制的实现。每一个程序模块中都有一个类加载器,当需要更新的时候,就将程序模块和其类加载器一起换掉。