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

一、什么是类加载机制

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

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

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

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

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

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

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

2、从哪个地方加载

加载的地方很多,比如:

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

二、类的生命周期

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

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对象具体的创建过程是什么?
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值