面试过程中被问到类加载的过程,看了这位兄台的文章,写的不错,在此做点补充。 http://blog.youkuaiyun.com/a1259109679/article/details/48085473
一、类的生命周期
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期按顺序包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)7个阶段,其中验证、准备、解析三个阶段统称为连接(Linking)。
二、类加载的全过程
(1) 加载
该过程需要完成三件事情:
通过一个类的全限名称来获取定义此类的二进制字节流
将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
(2) 验证
验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。这个阶段是否严谨,直接直接决定了Java虚拟机是否能承受恶意代码的攻击,如果验证道到的字节流不符合Class文件格式的约束,虚拟机就应抛出一个java.lang.VerifyError异常或其子类异常。验证阶段大致完成下面四个阶段的检验动作:文件格式验证、元数据验证、字节码验证、符号引用验证。
(3) 准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。强调:此时进行内存分配的仅包括内存变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。其次,这里所说的初始值“通常情况”下是数据类型的零值,假设一个类变量定义为: public static int value=123; 那么value在准备阶段过后的初始值为0而不是123,用于赋值的初始化方法在初始化阶段才会执行。
(4) 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。主要针对:类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
符号引用 | 符号(字面量) | 与虚拟机实现的内存布局无关 | 引用目标不一定加载到内存 |
直接引用 | 指针、相对偏移量、句柄 | 与虚拟机实现的内存布局相关 | 直接引用的目标在内存中一定存在 |
(5)初始化
这一阶段,真正开始执行类中定义的Java程序代码(或者说是字节码)。根据程序员通过程序制定的主观计划去初始化类变量和其他资源。(也可以说成:初始化阶段是执行类构造器<clinit>()方法的过程。)
三、类加载的双亲委派模型
直接上图:
双亲委派模型:
如果一个类加载器收到了类加载的请求,先把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,因此所有的加载请求最终都应该传送给顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时(没有找到所需的类,抛出ClassNotFoundException),子加载器才会尝试自己去加载(调用自己的findClass())。
优点:
Java类随着它的加载器一起具备了一种带有优先级的层次关系;保证Java程序的稳定运作;实现简单,逻辑清晰易懂;
四、类加载器执行子系统
tomcat服务器的类加载架构:
前三个为JDK默认提供的类加载器,而CommonClassLoader、CatalinaClassLoader、SharedClassLoader、WebAppClassLoader则是Tomcat自己定义的类加载器,他们分别加载/common/*、/server/*、/shared/*、/WebApp/WEB-INF/*中的类库,其中webapp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个jsp文件对应一个Jsp类加载器。
目前就这些,以后有了更深入的认识再进行补充~