简述
我们要运行java程序,必须保证jvm已经加载类所需的类。这个过程是怎样的呢
类的生命周期 加载将二进制字节码读入jvm。包括以下阶段:
1.通过类的全限定名获得定义此类的二进制数据流
2.将字节流转化为方法区的内部数据结构
3.在jvm堆中生成java.lang.Class实例,成为java程序与内部数据结构之间的接口
怎么生成二进制流?有很多种方式,例如本地文件系统加载,网络下载,jar提取,动态编译等。类加载器不需要等到某个类型初次时候才装载,可以预先加载,但是等到使用时,如果装载出现问题,才报错。
验证是为了保证Class文件字节流包含的信息符号jvm的要求,不会危害jvm运行的安全
包括几个方面:
1.文件格式验证。例如magic number,版本号 和 常量池数据合法性等
2.元数据验证。就是验证是否符合java语言规范。例如是否继承了final类等
3.字节码验证。涉及到指令的细节了。包括类型检查,跳转指令
4.引用符号的验证。验证通过符号能否找到相关的信息
jvm为类变量分配内存,设置默认的初始值(例如int 的默认初始值0)。假如有个类变量 int a = 1;在准备阶段,它的值是0.只有到了初始化阶段,它才真正被赋值1.而 static final int a = 1;又比较特殊,jvm在ConstantValue找到了它的值,所以就直接赋值为1
解析这个步骤是可选的。只有符号引用被程序首次使用时才进行。解析过程就是将符号引用转为直接引用的过程。包括类、接口、字段和方法的解析
初始化什么时候要初始化?
1.遇到new、 getstatic、putstatic或invokestatic四条指令。通俗一点讲,就是new一个对象、读取静态字段、设置静态字段(final的特殊,在准备阶段已经完成)或者调用某个类的静态方法。
2.其他特殊的创建对象方式(反射、克隆或者发序列化)
3.初始化子类,发现父类还没有被初始化,此事会先初始化父类
4.jvm启动时,用户指定要执行的主类(main函数所在的类)
初始化过程,就是给成员变量赋值的过程。类变量的初始化,和static语句块的执行都是在()函数中进行的。这个函数是jvm自动生成的,程序无法直接调用。我们看class文件的字节码就可以找到它。它里面代码的执行顺序是根据赋值语句在代码中出现的顺序依次执行。所以,static语句块只能访问它前面定义的变量。父类的()肯定在子类的()之前执行。接下来就执行构造函数了。
参考资料1.深入java虚拟机
2.深入理解java虚拟机