深入理解java虚拟机-4 虚拟机类加载机制

来源:互联网 发布:js添加input隐藏属性 编辑:程序博客网 时间:2024/05/13 04:02

在java语言中,类的加载,链接和初始化都是在运行期间发生的,所以java语言天生就可以实现动态拓展

1.类的生命周期

加载->验证->准备->解析->初始化->使用->卸载
验证+准备+解析 叫做连接(Linking)
类的生命周期共有7步,其中加载,验证,准备,使用,卸载的顺序是固定的,但是解析和初始化不是固定的,某些情况下解析可以在初始化后面进行

1.加载

加载过程中需要完成三件事情:
1.通过类的全限定名来获取此类的二进制字节流
2.将这个字节流代表的静态存储结构转化为方法区的运行时数据结构
3.在内存中生成java.lang.class对象,作为方法区这个类各种数据的访问入口(Class对象虽然是对象但是存在于方法区中)
加载过程中可以使用自己定义的加载器来控制字节流

2.验证

虽然通过java源码编译出来的字节流是相对安全的,但是也有可能字节流并不是通过源码编译出来的,而是经过恶意修改的,可能会使系统崩溃,所以java虚拟机要进行验证
验证过程有三步:
1.文件格式验证
2.元数据验证
3.字节码验证
4.符号引用验证

3.准备

准备阶段是正式为类变量分配内存并且设置类变量初始值的阶段
注意:
1.这里仅对类变量,static修饰的变量,而不是实例变量初始化.
2.这里的初始值是通常意义下的零值,如 public static Int value = 5; 经过准备过,value的值其实为0
3.特殊情况下:如public static final Internet value = 5;就会被设置为5;

4.解析

解析过程就是虚拟机将常量池里面的符号引用转换为直接引用,
虚拟机没有要求解析的时间,只要求了在 new .instanceof, getstatic等16个用于操作符号引用的字节码指令前解析
1.除invokedynamic外,对第一次解析的结果进行缓存(在运行时常量池中记录直接引用,并且将常量标记为已解析状态)
2.对于invokedynamic指令,他的引用为动态调用点限定符,等到执行时,才开始解析

常量池中有七种常量类型,下面有七种解析过程:

.....

5.初始化

java虚拟机规格规定了5种情况下,必须对类进行初始化
1.遇到new,getstatic,putstatic,或者invokestatic时,如果类没有进行初始化,则必须进行初始化
通常在下面情况下初始化:new一个实例时,调用类的静态方法,或者静态字段时(final标记的除外,他会在编译器就将结果放入常量池中)
2.使用java.util.reflect对类进行反射时
3.初始化一个实例时,如果父类还未初始化,先触发父类初始化
4.虚拟机启动,需要一个执行的主类(含有main方法的那一个),必须先初始化这个主类
5.使用jdk1.7的动态语言支持时,一个java.lang.invoke.MethodHandle实例最后解析结果是getstatic,putstatic.invokestati句柄,这个对应的类要先初始化

例子

package day20150908;public class LoadTest {static{System.out.println("LoadTest init");}public static void main(String[] args) {System.out.println(SubClass.value);}}class SuperClass{static{System.out.println("SuperClass init~");}public static int value =1; }class SubClass extends SuperClass{static{System.out.println("SubClass init~");}}

运行结果:
LoadTest init
SuperClass init~
1
main方法被执行前,先初始化loadtest类
因为只调用了父类的静态变量所以只需要初始化父类,并不需要初始化子类


类初始化是类加载过程的最后一步,前面的类加载过程中,处理加载阶段可以使用自己的加载器,其他都是虚拟机完成的,到了初始化这一步才是引用java程序代码
初始化过程是执行类构造器<Clint>()方法的过程


0 0
原创粉丝点击