类生命周期

类从被加载到虚拟机内存中开始到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证(Verification)、准备(Preparation)、解析(Resolution)3个部分统称为连接(Linking)。这7个阶段的顺序如图:
类的生命周期图
  加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类加载过程必须按照这个顺序开始,但是解析就不一定,因为Java存在运行时绑定。

加载

  加载阶段虚拟机需要完成三件事:
  1. 通过一个类的全限定名类获取定义此类的二进制字节流。
  2. 蒋这个字节流代表的静态存储结构转化成方法区的运行时数据结构。
  3. 在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

验证

  验证是连接阶段的第一步,目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证大致会完成以下4个校验动作:
  1. 文件格式验证。该验证目的是确保输入的字节流能正确的解析并存储于方法区之内,格式上符合描述一个Java类型信息的要求。
  2. 元数据验证。该验证目的是对类的元数据信息进行语义校验,保证不存在不符合Java语言规范的元数据信息。
  3. 字节码验证。该验证目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
  4. 符合引用验证。该验证目的是确保解析动作能正常执行。
  对应虚拟机的类加载机制来说,验证阶段是一个非常重要的,但不是一定必要的阶段。可以用-Xverify:none参数关闭大部分的类验证。

准备

  准备阶段是正式为类变量(被static修饰)分配内存并设置初始化值的阶段,这些变量所使用的内存都在方法区中进行分配。实例变量将会在对象实例化时随着对象一起分配在Java堆中。这里的初始化值是数据类型的零值,假设一个类的变量的定义为:
public static int value = 123;
那变量的value在准备阶段过后的初始值为0而不是123。把value赋值为123的putstatic指令是在程序被编译后,存放一个类构造器()方法中,所以把value赋值为123的动作将在初始化阶段才会执行。但是被static final修饰的类常量会在准备阶段赋值为可见值。

解析

  解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
  符号引用:以一组符号来描述引用的目标。 
  直接引用:直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。

初始化

  类初始化是类加载最后的一个阶段。初始化阶段是执行类构造器()方法的过程。()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集顺序有语句在源文件中出现的顺序所决定,静态语句块只能访问到定义在静态语句块之前的变量,定义在之后的变量,在前面的静态语句块可以赋值,但是不能访问。()不需要显示调用构造器,与实例构造器()方法不同。用以下代码说明一下类变量和实例变量初始化:

public class ClassLoadTest {
    static ClassLoadTest t = new ClassLoadTest();
    static{
        System.out.println("1");
    }
    {
        System.out.println("2");
    }
    public ClassLoadTest(){
        System.out.println("3");
        System.out.println("a=" + a + ";b=" + b);
    }
    public static void f(){
        System.out.println("4");
    }
    static int b = 150;
    int a = 100;
    public static void main(String[] args) {
        f();
    }
}

上面程序执行的结果:

2
3
a=100;b=0
1
4

对应的初始化顺序如下:

public class ClassLoadTest {
    static ClassLoadTest t = new ClassLoadTest();
    public ClassLoadTest(){
        {
            System.out.println("2");
        }
        int a = 100;
        System.out.println("3");
        System.out.println("a=" + a + ";b=" + b);
    }
    static int b;
    static{
        System.out.println("1");
         b = 150;
    }
    public static void f(){
        System.out.println("4");
    }
    public static void main(String[] args) {
        f();
    }
}
posted @ 2018-01-01 20:06  Griez  阅读(174)  评论(0编辑  收藏  举报