欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JVM类加载机制,java类的加载时机

程序员文章站 2022-05-12 18:34:34
参考https://baijiahao.baidu.com/s?id=1636309817155065432&wfr=spider&for=pchttps://blog.csdn.net/zhangliangzi/article/details/51319033一、什么是类的加载1、什么时候进行类的加载2、从哪个地方加载二、类的加载过程......

一、什么是类加载机制

在说类加载机制之前,先看下一张jvm加载类的大致流程图,方便理解。
JVM类加载机制,java类的加载时机
由上图可以看出,java文件通过编辑器编译生成了class文件,接下来的 类装载器 环节,就是我们所说的 类加载机制

类加载机制 其实就是: 首先通过 类加载器 将class文件加载到内存中,然后对其进行 验证、准备、解析、初始化,使这些数据最终成为可以被JVM直接使用的Java类型,这个过程就是 JVM类加载机制

1、什么时候进行类的加载

什么时候进行类的 加载 ?相信很多人在面试的时候都遇到过这个问题。

我们平常所说类的加载 主要指的是 类加载机制 的第一个步骤。类加载机制 包括了 加载验证、准备、解析、初始化这几个步骤。当然还有最后一个步骤 卸载。关于这几个步骤,后面会详述。

当应用程序启动的时候,所有的类会被一起加载吗?当然不可能,毕竟系统的内存资源是有限的。那什么时候才会被加载呢?

一般某个类 首次主动使用 的时候会被加载。类的 加载类加载机制 的第一个步骤,相对其他几个步骤,它是可控性最强的一个阶段,因为我们可以使用系统的类加载器加载,也可以使用自己自定义的类加载器进行加载。同时我们还可以进行 预加载,不需要等到某个类 首次主动使用 的时候再去加载。当然,如果 预加载 过程中出现了异常,类加载器必须在程序首次主动使用该类的时候才抛出异常,如果该类一直没有被主动使用,类加载器就不会抛出异常。

2、从哪个地方加载

加载的地方很多,比如:

  • 本地磁盘
  • 网络上加载
  • 从其他文件生成的

二、类的生命周期

类从加载到虚拟机内存开始,到从虚拟机中卸载结束一共包含了 七个生命周期阶段:加载、验证、准备、解析、初始化、使用、卸载。类的加载机制包含了前5个阶段,如下图:
JVM类加载机制,java类的加载时机
链接 分为 验证、准备、解析 三个步骤。

1、加载

  • 将class文件加载在内存中。这里就是通过类加载机制来加载的。详细请看博客:https://blog.csdn.net/tongsiw/article/details/79939663
  • 将静态存储结构(数据存在于class文件的结构)转化为方法区的运行时数据结构(数据存在于JVM时的数据结构)。这里只是转化了数据结构,并未合并数据。(方法区就是用来存放已被加载的类信息,常量,静态变量,编译后的代码的运行时内存区域)。
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。这个Class对象并没有规定是在Java堆内存中,它比较特殊,虽为对象,但存放在方法区中。

2、链接

链接就是将Java类的二进制代码数据合并到Java运行时环境中。类的链接大致分三个阶段:

  • 验证: 确保加载的类符合JVM规范与安全
  • 准备: 为类的 静态变量(static filed) 在方法区分配内存,并赋默认初值(0值或null值)。如 static int a = 1 ,静态变量 a 会在 准备 阶段 被赋默认值0。
    静态常量(static final filed) 会被直接赋值。如 static final int b = 1,静态常量 b 会在准备阶段北直接赋值1。
  • 解析: 虚拟机将常量池的符号引用转变成直接引用。

3、初始化

这是类加载机制的最后一步,在这个阶段,java程序代码才开始真正执行。我们知道,在准备阶段,会给静态变量 赋初始值,在初始化阶段,就是赋真正的值了,如上 static int a = 1 ,静态变量 a准备 阶段被被赋默认值0,在初始化阶段就会被赋值1。

初始化阶段总结一句话就是:对类变量赋予正确的值。

初始化一个类,包含两个步骤:

  • 如果存在父类,先初始化父类。
  • 如果类中有初始化语句,则系统依次执行这些初始化语句。

**初始化时机:**只有当类被主动引用的时候,才会被初始化。

类的主动引用:

  • new一个对象。
  • 调用类的静态成员(除了final常量)和静态方法。
  • 通过反射(如 Class.forName(“com.xx.Xx”))对类进行调用。
  • 虚拟机启动,main方法所在类被提前初始化。
  • 初始化一个类,如果其父类没有初始化,则先初始化父类。

类的被动引用:
除了上面的几种类的主动引用方式,剩下的就是 类的被动应用。类的被动应用不会触发类的初始化。

根据类的主动引用那几种方式,类的被动引用有以下几个实例:

  • new一个对象数组,注意是对象数组而不是对象,如 Object obj = new Object[10],这个时候也不会触发类的初始化。
  • 调用一个类中final修饰的常量,是不会触发 类的初始化的。
  • 通过子类引用父类的静态字段,父类会被初始化,子类是不会触发初始化的

看如下代码示例:

父类

public class Father {
    public static int value = 1;
    public static final int finalValue = 2;
    static {
        System.out.println("father init");
    }
} 

子类

public class Son extends Father {
    static {
        System.out.println("son init");
    }
} 

测试类

public class Test {
    public static void main(String[] args) {
        System.out.println("开始验证new一个对象数组,不会触发类的初始化");
        Father[] fathers = new Father[10];

        System.out.println("\n\n开始验证调用一个类的final修饰常量,不会触发类的初始化");
        System.out.println(Son.finalValue);

        System.out.println("\n\n开始验证通过子类引用父类的静态字段,父类会被初始化,子类是不会触发初始化");
        System.out.println(Son.value);
    }
} 

打印结果

开始验证new一个对象数组,不会触发类的初始化


开始验证调用一个类的final修饰常量,不会触发类的初始化
2


开始验证通过子类引用父类的静态字段,父类会被初始化,子类是不会触发初始化
father init
1 

可以看出 new Father[10]数组是不会触发Father类初始化的,因为没有打印出father init 这条语句。

同理,调用一个类的final修饰常量,也是不会触发类的初始化的。

4、使用

初始化之后,jvm开始执行用户的程序代码

5、卸载

当用户程序代码执行完毕后,JVM 便开始销毁创建的 Class 对象,最后运行的 JVM 也退出内存。

//TODO
这里类加载机制中 类的初始化和类的实例化 有什么区别呢?

java new 之后具体还有哪些过程?Java对象具体的创建过程是什么?

本文地址:https://blog.csdn.net/tongsiw/article/details/108029325