1、class
装载验证流程
加载:
装载类的第一个阶段;
取得类的二进制流;
转为方法区的数据结构;
在java堆中生成对应的java.lang.Class对象;
链接(验证、准备、解析):
验证:
目的:保证Class流的格式是否正确;
文件格式的验证:版本号、文件头等;
元数据是否合理:是否有父类;继承final类?;非抽象类实现了所有的抽象方法?
字节码验证(很复杂):运行检查;栈数据类型和操作码数据参数吻合;跳转指令到合理的位置;
准备:分配内存,并为类设置初始值(方法区中);
public static int v=1;在准备阶段中,v会被设置为0;在初始化的<clinit>中才会被设置为 1;
对于static final类型,在准备阶段就会被赋上正确的值,public static final int v=1;
解析:为
方法区常量池里的常量,将符号引用替换为直接引用;
符号引用,字符串引用对象不一定被加载;
直接饮用:指针或地址偏移量,引用对象一定在内存中;
(
解析不一定非要在准备之后初始化之前进行,
这个阶段的主要任务是使用阶段才会用到的,如果程序中有动态绑定的需求时这时候是没有办法把符号引用准确转换为直接引用的。)
初始化:
执行类构造器<clinit>
static变量,赋值语句;
static{}语句;
子类的<clinit>调用前保证父类的<clinit>被调用
<clinit>是线程安全的;
后续的是:
使用、
卸载;
我们在文件里写入了java的源代码,源代码写就后存入磁盘,磁盘上的
源代码
经过javac命令的编译形成了
二进制字节码形成了class文件
,经过一番步骤后java虚拟机将这些二进制字节码按照一定的方式读入内存中的不同区域形成了
二进制字节码的活化状态
,虚拟机使用字节码指定的命令执行这些指令,其间使用字节码中存储的数据,最终完成了任务。这个过程就是java虚拟机执行java二进制字节码的过程的简单概括。可以如下图所示:
.java--编译-->.class--类加载-->活化字节码--执行-->
类加载:把二进制字节码转为字节流,把它从磁盘里class文件里的二进制字节码将会成为内存不同区域里的数据,虚拟机将这些数据代表的指令执行完成任务。
以上几个步骤就是类加载的全过程,在这个过程中,class文件中的二进制字节码以二进制字节流的形式, 先按照方法区特定的数据结构重整并建立java.lang.Class对象于堆中,
验证重整后的二进制字节流没有语法、语意和安全性的问题后虚拟机为即将加载的类在方法区中开辟内存空间,
字节流注入开辟的方法区的内存空间并将各字段赋零值,常量池中的符号引用转换为有实际意义的直接引用以访问特定的地址,
特定的字段被初始化为程序规定的初值,整个类成功加载到方法区中。
java虚拟机对于类加载过程的初始化的时机,有明确的四个“有且仅有要求”,这些条件下必须对类进行初始化:
new新对象、读写静态字段、调用静态方法的时候必须初始化类:读写静态字段时只初始化这个静态字段所在的类, 如果是父类的静态字段则只初始化父类而不初始化字类;
另外如果是static final修饰的静态字段,那么在编译的时候就会将其写入常量池,这个时候即使读这个静态字段也不会加载类,因为只需要去常量池中取这个值就好。
这两个策略的目的其实都是尽可能地减少类加载的开销;
反射调用的时候初始化类;
初始化类的时候如果父类未初始化要初始化父类;
执行主类(执行的main函数所在类)要初始化。
2、什么是类装载器ClassLoader?
ClassLoader是一个抽象类;
ClassLoader的实例将读入java字节码将类装载到jvm中;
ClassLoader可以定制,满足不同的字节码流获取方式;
ClassLoader负责类装载过程中的
加载阶段;
重要方法:loadClass(); defineClass(); findClass(); findLoadedClass();
3、JDK中ClassLoader默认设计模式
BootStrap ClassLoader (启动):rt.jar包等
Extension Classload(扩展):ext包等
App ClassLoad(应用/ 系统): 应用程序下的大多包:Classpath下的类
Customer ClassLoader(自定义)
每个ClassLoad都有一个Parent作为父亲,bootStrp是例外;
查找类方向:自底向上检查类是否已加载:从App --> Bootstrap(父加载器)
加载类方向:自上向底加载:Bootstap --> App;
手工指定bootstap路径:-Xbootclasspath/a:D:/tmp/clz
可以强行加载class,通过反射,强行设置class的Accessble为true,初始化对象
*双亲委派模式问题:顶层ClassLoad-APP无法加载底层ClassLoader-Bootstrap的类;
解决办法:Thread.setContextclassLoader():上下文加载器,是一个角色,用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题;基本思想,在顶层ClassLoader中传入底层ClassLoader的实例;
4、打破常规模式
双亲模式是默认的模式,但不是必须这么做;
tomcat的WebappClassLoader就会先加载自己的class,找不到再委托parent;
OSGI的classloader形成网状结构,根据需要自由加载class
5、热替换