深入理解JVM(六)——类加载器原理

本文深入探讨了Java虚拟机(JVM)中的类加载过程,包括加载、验证、准备、初始化和卸载五个阶段,详细解释了双亲委派机制及其在确保类加载安全性方面的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:深入理解JVM(六)——类加载器原理
深入理解JVM(一)JVM基本原理和类的加载
java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了。
那么字节码文件是怎样装载到JVM中的呢?中间经过了哪些步骤?常说的双亲委派模式又是怎么回事?本文主要搞清楚这些问题。

类装载流程

类装载流程类的加载过程必须按照 加载、验证、准备、初始化、卸载 的顺序开始,而解析阶段则不一定,他在某些情况下可以在初始化阶段之后开始。

1、加载

类的装载,首先通过class文件的路径读取到二进制流,并解析二进制流将里面的元数据(类型、常量等)载入到方法区,在java堆中生成对应的java.lang.Class对象。

第一步:获取二进制字节流(class文件)。
第二步:将静态的存储结构转换为方法区中的运行时数据结构。
第三步:生成一个对象放入java堆中,做为对方法区的引用

2、连接

连接过程又分为3步:
2.1、验证
验证的主要目的就是判断class文件的合法性,比如class文件一定是以0xCAFEBABE开头的,另外对版本号也会做验证,例如如果使用java1.8编译后的class文件要再java1.6虚拟机上运行,因为版本问题就会验证不通过。

class文件(字段表、方法表等),验证常量池(常量类型、常量类型数据结构是否正确,utf-8是否标准),元数据验证(父类验证,继承验证,final验证),字节码(指令)验证,符号引用验证(是否能根据符号找到对应的字段、表、方法等)

2.2、准备
准备过程就是分配内存,给类的一些字段设置初始值,例如:

 public static int v=1;

这段代码在准备阶段v的值就会被初始化为0,只有到后面类初始化阶段时才会被设置为1

但是对于static final(常量,直接对应到常量池中),在准备阶段就会被设置成指定的值,例如:

public static final  int v=1;

这段代码在准备阶段v的值就是1。

2.3、解析

解析过程就是将符号引用替换为直接引用(指向目标的指针或者偏移量),例如某个类继承java.lang.object,原来的符号引用记录的是“java.lang.object”这个符号,凭借这个符号并不能找到java.lang.object这个对象在哪里?而直接引用就是要找到java.lang.object所在的内存地址,建立直接引用关系,这样就方便查询到具体对象。
主要涉及到的解析有类,接口,字段,方法等。

3、初始化

初始化过程,主要包括执行类构造方法、static变量赋值语句,staic{}语句块,需要注意的是如果一个子类进行初始化,那么它会事先初始化其父类,保证父类在子类之前被初始化。所以其实在java中初始化一个类,那么必然是先初始化java.lang.Object,因为所有的java类都继承自java.lang.Object。

类加载过程中的主角:类加载器。

类加载器

类加载器ClassLoader,它是一个抽象类,ClassLoader的具体实例负责把java字节码读取到JVM当中,ClassLoader还可以定制以满足不同字节码流的加载方式,比如从网络加载、从文件加载。ClassLoader的负责整个类装载流程中的“加载”阶段。

ClassLoader的重要方法:

  public Class<?> loadClass(String name) throws ClassNotFoundException//载入并返回一个类。
  protected final Class<?> defineClass(byte[] b, int off, int len)//  定义一个类,该方法不公开被调用。
  protected Class<?> findClass(String name) throws ClassNotFoundException//  查找类,loadClass的回调方法
  protected final Class<?> findLoadedClass(String name)// 查找已经加载的类。

系统中的ClassLoader

BootStrap Classloader (启动ClassLoader)
Extension ClassLoader (扩展ClassLoader)
App ClassLoader(应用 ClassLoader)
Custom ClassLoader(自定义ClassLoader)

每个ClassLoader都有另外一个ClassLoader作为父ClassLoader,BootStrap Classloader除外,它没有父Classloader。

ClassLoader加载机制

ClassLoader加载机制
自下向上检查类是否被加载,一般情况下,首先从App ClassLoader中调用findLoadedClass方法查看是否已经加载,如果没有加载,则会交给父类,Extension ClassLoader去查看是否加载,还没加载,则再调用其父类,BootstrapClassLoader查看是否已经加载,如果仍然没有,自顶向下尝试加载类,那么从 Bootstrap ClassLoader到 App ClassLoader依次尝试加载。

值得注意的是即使两个类来源于相同的class文件,如果使用不同的类加载器加载,加载后的对象是完全不同的,这个不同反应在对象的 equals()、isAssignableFrom()、isInstance()等方法的返回结果,也包括了使用 instanceof 关键字对对象所属关系的判定结果。

图片描述
从代码上可以看出,首先查看这个类是否被加载,如果没有则调用父类的loadClass方法,直到BootstrapClassLoader(没有父类),我们把这个过程叫做双亲模式,

为什么有自己的加载器,干嘛还要去找父类的加载器呢?这种机制叫双亲委任机制,目的就是为了加载类的安全。也就是说我父类加载的不给子类去加载,这样保证最终都是BootstarpClassLoader去加载,可以保证一个类只会加载一次。判断两个对象是否一样的时候,最重要的一个条件就是 是不是同一个加载器去加载的。

双亲模式的问题

顶层ClassLoader,无法加载底层ClassLoader的类

Java框架(rt.jar)如何加载应用的类?

比如:javax.xml.parsers包中定义了xml解析的类接口
Service Provider Interface SPI 位于rt.jar
即接口在启动ClassLoader中。
而SPI的实现类,在AppLoader。
这样就无法用BootstrapClassLoader去加载SPI的实现类。

解决
JDK中提供了一个方法:

  1:  Thread. setContextClassLoader()

用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题;基本思想是,在顶层ClassLoader中,传入底层ClassLoader的实例。

双亲模式的破坏

双亲模式是默认的模式,但不是必须这么做;
Tomcat的WebappClassLoader 就会先加载自己的Class,找不到再委托parent;
OSGi的ClassLoader形成网状结构,根据需要自由加载Class。

一、综合实战—使用极轴追踪方式绘制信号灯 实战目标:利用对象捕捉追踪和极轴追踪功能创建信号灯图形 技术要点:结合两种追踪方式实现精确绘图,适用于工程制图中需要精确定位的场景 1. 切换至AutoCAD 操作步骤: 启动AutoCAD 2016软件 打开随书光盘中的素材文件 确认工作空间为"草图与注释"模式 2. 绘图设置 1)草图设置对话框 打开方式:通过"工具→绘图设置"菜单命令 功能定位:该对话框包含捕捉、追踪等核心绘图辅助功能设置 2)对象捕捉设置 关键配置: 启用对象捕捉(F3快捷键) 启用对象捕捉追踪(F11快捷键) 勾选端点、中心、圆心、象限点等常用捕捉模式 追踪原理:命令执行时悬停光标可显示追踪矢量,再次悬停可停止追踪 3)极轴追踪设置 参数设置: 启用极轴追踪功能 设置角度增量为45度 确认后退出对话框 3. 绘制信号灯 1)绘制圆形 执行命令:"绘图→圆→圆心、半径"命令 绘制过程: 使用对象捕捉追踪定位矩形中心作为圆心 输入半径值30并按Enter确认 通过象限点捕捉确保圆形位置准确 2)绘制直线 操作要点: 选择"绘图→直线"命令 捕捉矩形上边中点作为起点 捕捉圆的上象限点作为终点 按Enter结束当前直线命令 重复技巧: 按Enter可重复最近使用的直线命令 通过圆心捕捉和极轴追踪绘制放射状直线 最终形成完整的信号灯指示图案 3)完成绘制 验证要点: 检查所有直线是否准确连接圆心和象限点 确认极轴追踪的45度增量是否体现 保存绘图文件(快捷键Ctrl+S)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值