JVM学习笔记4:类加载机制

第七章 虚拟机类加载机制

7.1 概述

类加载机制:Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型的过程

7.2 类加载时机

在这里插入图片描述

  • . 加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载过程必须按照这种顺序开始,互相交叉论和进行
  • . 解析阶段:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定特性(也称为动态绑定或晚期绑定)

**初始化:**有且仅有下述6个情况必须立即进行初始化(加载、验证、准备在此之前)

  • new、getstatic、putstatic或invokestatic这四条字节码指令时
    • 使用new关键字实例化对象
    • 读取或设置一个类型的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)
    • 调用一个类型的静态方法
  • 使用java.lang.reflect包的方法对类型进行反射调用时
  • 当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
  • 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类
  • 当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化
  • 当一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

上述六个阶段称为对一个类型的主动引用
除此之外的引用都不会触发初始化,称为被动引用

7.3 类加载的过程

7.3.1 加载

  1. 通过一个类的全限定名(全类名)获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

非数组:可由引导类加载器完成或自定义类加载器完成
数组类:由Java虚拟机直接在内存中动态构造出来的,数组类的元素类型由类加载器完成
数组的组件类型(数组去掉一个维度)是引用类型,采用上述方案递归加载这个组件类型,数组C将被标识在加载该组件类型的类加载器的类名称空间上
否则,Java虚拟机将会把数组C标记为与引导类加载器关联。

7.3.2 验证

  • 确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机
  • 包括四种验证:
    • 文件格式验证:魔数、版本号等等,验证后输入的字节流将解析并存储于方法区内,以下直接在方法区中进行
    • 元数据验证:语义分析,主要对类进行
    • 字节码验证:确定程序语义合法、符合逻辑,对方法体进行
    • 符号引用验证:在虚拟机将符号引用转化为直接引用时(解析阶段)发生:该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源

7.3.3 准备

  • 为类变量(static)分配内存并默认初始化
  • 不包含常量,常量在编译时就会分配内存,准备阶段显式初始化
  • 不会为实例变量分配初始化,实例变量随着类分配到堆中

7.3.4 解析

  • 将常量池内的符号引用转换为直接引用(指针、相对偏移量、定位句柄)
  • 一般JVM执行完初始化后再解析
  • 针对类或接口,字段,类方法,接口方法,方法类型等

7.3.5 初始化

  • 就是执行类构造器方法< clinit >()的过程
  • 此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而成(没有则无此方法)
  • 构造器方法中指令按语句在源文件中出现的顺序执行
  • 类的构造器是虚拟机视角下的< init>()函数
  • 该类存在父类,先运行父类的< clinit >()完毕后再执行子类
  • JVM必须保证一个类的< clinit >()方法在多线程下被同步加锁(一个类只会被加载一次)

7.4 类加载器

引导类加载器Bootstrap Class Loader(c和c++编写)–>扩展类Extension Class Loader–>系统类System Class Loader
三者是包含关系,可以看作目录
引导类加载器Bootstrap Class Loader

  • c/c++编写,在JVM内部
  • 加载Java核心类库(核心类在打印其加载器时会是null)
  • 不继承自java.lang.ClassLoader,没有父类
  • 是扩展类和系统类的父类

扩展类加载器Extension Class Loader

  • Java编写
  • 间接继承于ClassLoader(抽象类)
  • 父类为启动类加载器
  • 用户创建的jar包放在指定目录下也可以由扩展类加载器加载

系统类加载器System Class Loader

  • Java编写
  • 间接继承于ClassLoader(抽象类)
  • 父类为扩展类加载器
  • 用户自定义类由系统类加载器加载

用户自定义类加载器

  • 隔离加载类
  • 修改类加载的方式
  • 扩展加载源
  • 防止源码泄露

ClassLoader
抽象类,除了启动类加载器之外所有的类加载器都继承该类
在这里插入图片描述
获取类加载器
在这里插入图片描述
双亲委派机制
加载器收到类加载请求,先把请求交给父类加载器执行,依次向上,最终请求到达引导类加载器,父类可以则父类加载,否则子类加载
避免类的重复加载
保护程序安全,防止核心API被篡改(沙箱安全机制)

破坏双亲委派机制
1.引入双亲委派机制之前存在的用户自定义类加载器代码
2.某些基础类型需要调用一些其他代码,使用线程上下文类加载器,这是父类加载器请求子类加载器完成加载的行为
3.用户对程序动态性的追求而导致的:如OSGi

其他问题

  • 两个class对象是否相同:来源同一个Class文件,被同一个虚拟机加载,加载器相同
  • 一个类由用户类加载器加载,这个类加载器的引用会作为类型信息的一部分保存到方法区,解析一个类型到另一个类型时,JVM要保证这两个类型的类加载器相同

7.5 Java模块化系统

JDK9引入,为了可配置的封装隔离机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值