Java虚拟机 -类的加载

Java虚拟机结束生命周期

  • 执行了System.exit()方法
  • 程序正常执行结束
  • 程序在执行过程中遇到了异常或错误而异常终止
  • 由于操作系统出现错误而导致Java虚拟机进程终止

Java代码中,类型的生命周期

加载

  • 类的加载指的是将类的.class文件中的二进制读入到内存中,将其放在运行时数据区的方法区内(JDK1.8的元空间),然后在内存中创建一个java.lang.Class对象用来封装类在方法区内的数据结构。
  • 类的加载的最终产品是存在内存中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

加载.class文件的方式

  • 从本地系统直接加载
  • 通过网络下载.class文件
  • 从zip,jar等归档文件中加载.class文件
  • 从专有数据库中提取.class文件
  • 将Java源文件动态编译为.class文件

连接

将已经读入内存的类的二进制数据合并到虚拟机的运行时环境中。连接过程分为三个阶段。

验证:确保被加载类的正确性

  • 类文件的结构检查
  • 语义检查
  • 字节码验证
  • 二进制兼容性的验证

准备:为类的静态变量分配内存,并将其初始化为默认值。

解析:在类型的常量池中寻找类、接口、字段和方法的符号引用,将符号引用替换成直接引用。

初始化

初始化会为类的静态变量赋予正确的初始值。

  • 假如这个类还没有被加载和连接,那就先进行加载和连接。
  • 假如类存在直接父类,并且这个父类还没有被初始化,那就先初始化直接父类。
  • 假如类中存在初始化语句,那就依次执行这些初始化语句。在程序中静态变量的初始化有两种途径,在静态变量的声明处进行初始化,以及在静态代码块中进行初始化。
  • 一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化。
  • 只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用。
  • 调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化。
  • 常量(final修饰)在编译阶段会存入到调用这个常量的方法所在的类的常量池中,调用类并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中,程序运行时会主动使用这个常量所在的类导致此类被初始化。

类的初始化时机

Java程序对类的使用方式分为主动使用和被动使用,所有的Java虚拟机实现必须在每个类或接口被Java程序首次主动使用时才初始化它们。

主动使用

  • 创建类的实例
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射,反射获取类的对象是对类的主动使用
  • 初始化一个类的子类
  • Java虚拟机启动时被标名为启动类的类(main)
  • JDK1.7开始提供的动态语言支持

使用

程序通过类来创建对象,调用类的方法。

类实例化过程

  • 为新的对象分配内存
  • 为实例变量赋默认值
  • 为实例变量赋正确的初始值
  • java编译器为它编译的每一个类都至少生成一个实例初始化方法,在java的class文件中,这个实例化方法被称为"<init>"。针对源代码中每一个类的构造方法,java编译器都产生一个方法。

卸载

  • 当代表类的Class对象不再被引用,即不可触及时,Class对象就会结束生命周期,类在方法区内的数据也会被卸载。
  • 由Java虚拟机自带的 类加载器所加载的类,在虚拟机的声明周期中,始终不会被卸载。由用户自定义的类加载器所加载的类是可以被卸载的。
  • 调用System.gc()手动卸载声明周期结束的类。
posted @ 2021-04-01 14:58  这是一个ID  阅读(59)  评论(0)    收藏  举报