参考自深入理解 Java 虚拟机
把描述类的数据从 Class 文件加载到内存中,然后对数据进行校验,解析,初始化最终形成能够被虚拟机使用的类,这就是虚拟机的类加载机制
类的生命周期:类从被加载到虚拟机中到被卸载出内存为止一共要经历 7 个阶段:加载,验证,准备,解析,初始化,使用,卸载。其中加载,验证,准备这 3 个阶段是按部就班的开始,但是解析阶段不一定,它也可能在初始化阶段后才发生,这是为了支持 java 的运行时的动态绑定 Java 虚拟机规范没有规定在什么情况下对类进行加载,这一点由具体的虚拟机实现来自由把握,但是对于初始化,虚拟机规定以下几种情况下必须对类进行初始化 1. 当调用类中的静态字段或者是静态方法的时候 2. 当实例化一个对象的时候 3. 利用反射操作一个类的时候 4. 当一个类进行初始化的时候,如果它的父类还未初始化首先要对它的父类进行初始化 5. 虚拟机启动的时候需要一个执行类(含有 main 方法的类),这个执行类会被初始化
类加载的阶段要做三件事情 1. 通过类的全限定类名(比如 java.lang.String)来获取定义此类的二进制字节流 (由于这个阶段 Java 虚拟机规范并没有规定一定要从哪里获取字节流,所以一些五花八门的获取字节流的方式就诞生了,开发者可以通过实现自己的自定义类加载来实现,这也促进了一些举足轻重的 java 技术的诞生,比如从 Zip 中获取最终成为日后 JAR,EAR,WAR 格式的基础,从网络中获取对应着之前流行过的 Applet,运行时动态生成这种场景使用最多的就是动态代理技术,还有其他的更多的方式,读者可以发挥自己的想象力创造出更多的新技术。由此可见 Java 虚拟机规范对加载阶段的开放是很正确的选择) 2. 把描述类的静态存储结构(字节码)从 class 文件中加载到内存然后转化成运行时方法区的数据结构 3. 生成一个 java.lang.Class 对象(常用于反射技术)作为访问该类在方法区的数据的访问入口
验证阶段的目的主要是验证字节码中的数据是否符合虚拟机规范的要求,并且不会危害虚拟机的安全,验证也是虚拟机对自身保护的一个重要的工作
文件格式的验证主要就是验证一下几个部分: 1. 是否以魔数 0xCAFEBABE 开头(class 文件中数据的起始部分固定为魔数,为了防止其他文件的后缀被篡改为. class 文件) 2. 版本号是否在该虚拟机的处理范围之内(高版本的 jdk 编译的字节码,低版本的 jdk 无法处理) 3. 常量池中的常量中是否有不被支持的类型 …….
该阶段的主要目的就是对类的元数据信息进行语义的校验,保证不存在不符合 java 虚拟机规范的元数据信息,比如说要校验一个类是否有父类(Java 中每个类都有父类,默认继承 Object 类),校验这个类是否继承了不被允许继承的类(被 final 修饰的类),一个类是否实现了它所实现的接口中要求实现的方法等等
第二阶段是对元数据进行验证,该阶段是对类的方法体进行验证,保证不会出现这样的情况: 向操作数栈中存放了一个 int 类型的数据,使用时却按照 long 类型的数据加载到本地变量表中 保证跳转指令不会跳转到方法体以外的字节码指令上 保证类型转换是有效的,不会出现把两个毫不相干的类互相强制转换的情况
符号引用验证发生在虚拟机把符号引用转换成直接引用的时候,通常要验证以下内容: 符号引用中通过全限定类名能够找到对应的类 在指定的类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段 符号引用的访问性(private,protected,public,default)是否可被当前类访问 验证阶段所要验证的一些内容现在的编辑器比如 eclipse 都会帮我们做了,但是也无法强制所有的开发者必须通过这些比较智能的编辑器来编写 java 程序,所以该验证阶段还是必不可少
准备阶段要做的事就是为类变量分配内存并设置初始值,注意该初始值并不是开发者为类变量赋的初始值,而是系统初始值,比如 public static int a=3; 类变量 a 在准备阶段被设置的应为系统初始值 0,而不是 3,类变量 a 会在初始化阶段被赋值为 3
解析阶段就是将符号引用替换为直接引用的过程
初始化阶段是类加载全过程的最后一步,初始化阶段的唯一要干的事情就是执行类构造器 () 方法,这个方法并不是类的构造方法,那么这个方法是怎么来的呢?它是虚拟机收集类中所有的静态块,和静态字段赋值语句而生成的方法,也就是执行完该方法才真正的为类变量赋上了初始值。
浏览( 36 ) 评论( 0 ) 2017-12-30 05-14-56
来源: http://www.zhuchengzhong.cc/blog/detail/18.do