[笔记] 深入java虚拟机 类型的生命周期

类型生命周期开始

装载(将该二进制形式的java类型读入java虚拟机) --> 连接(分为三个步骤,验证(确认java类型数据格式正确且适于java虚拟机使用),准备(为类型分配内存),解析(将常量池中的符号引用转换为直接引用)) --> 初始化(类型初次使用或子类被初始化)

 

装载

通过该类型的完全限定名,产生一个代表该类型的二进制数据流;解析二进制数据流为方法区的内部数据结构,创建一个表示该类型的java.lang.Class类的实例

 

连接

验证

检查魔数,确保每一个部分都在正确的位置拥有正确的长度等,确保出Object外每一个类都有一个超类;等方法,类格式正确

准备

解析

在类型的常量池中寻找类,接口,字段和方法的符号引用,把这些符号引用替换成直接引用的过程

 

初始化

先初始化父类,然后执行类初始化方法,执行类变量初始化语句和类型的静态初始化语句,这两个语句被java编译器收集放到一个特殊的方法中,成为类初始化方法<clinit>,只能被虚拟机调用。如果不存在类变量和静态初始化语句或者仅包换static final变量的初始化语句(编译时常量表达式)或者存在类变量但是没有使用静态初始化语句,都不会产生<clinit>,首次使用时也不会执行类初始化。

class Dog{

  static final String greeting = "world";

  static{

    System.out.println("hello');

  }

}

Class Example{

  public static void main(String[] args){

    System.out.println(Dog.greeting);

  }

}

输出: world

说明类Dog并没有执行类初始化

 

接口中的public,static,final字段必须在字段初始化语句中初始化,如果接口包含任何在编译时被解析成为一个常量的字段初始化语句,接口就会拥有一个clinit方法

interface Examplelf{

  int ketchup=5;

  int ustard=(int)(Math.ramdom()*0.5);

}

java编译器会为上述interface生成一个clinit方法,而只有ustard在clinit中初始化,因为ketchup被初始化为一个编译时常量,而Examplelf.ustard的类型保存指向这个字段的符号引用,使用Examplelf.ketchup的类型会保存5的一份本地copy

 

类实例化

类构造函数

Class Example{
   Example(){
    this(1); //调用其他构造函数
  }
  Example(int i){
    //默认调用父类的无参数构造函数
  }
  Example(String s){
    super();//显示调用父类的构造函数
  }
}

垃圾收集和对象的终结

由虚拟机回收对象,如果类声明来一个finalize方法,则在释放实例内存前会执行这个终结方法,但是只会调用一次,如果对象复活之后不再被引用,终结方法不会再被调用,且终结方法抛出的任何异常会忽略。

Class example{
  protected void finalize(){
  }
}

卸载类型

启动类装载器装载的类永远是可触及的,不会被卸载,只有用户定义的类装载起装载的类才可被虚拟机回收。

 

原文地址:https://www.cnblogs.com/zengyou/p/2557405.html