JAVA类加载机制

类的生命周期

类的生命周期分为以下7个阶段:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载,其中验证、准备、解析阶段又统称为连接

类的生命周期

一、加载

加载阶段有以下三个步骤:

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

PS:在HotSpot中,第三阶段生成的Class对象是保存在方法区的。

二、验证

验证是连接阶段的第一步,这个阶段的目的是检查字节流中包含的信息是否符合虚拟机的要求,确保不会危害虚拟机自身的安全。它包括如下四个方面的验证:文件格式验证元数据验证字节码验证符号引用验证

1.文件格式验证

第一阶段要验证字节流是否符合Class文件格式的规范,并且能够被虚拟机处理,要验证的方面有:

是否以魔数0xCAFEBABE开头
主、次版本号是否在当前虚拟机处理范围内
常量池中的常量是否有不被支持的常量类型
指向常量的索引值是否有不存在的常量或者不符合类型的常量
CONSTANT_Utf8_info型的常量是否符合UTF-8编码格式
......
除了以上这些,还有很多需要验证的地方,上面只是一小部分。通过了这个阶段的验证,字节流中的静态数据结构会保存在方法区中,后面三个阶段的验证都是基于方法区的存储结构进行的。

2.元数据验证

第二阶段对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求,要验证的方面有:

这个类是否具有父类
这个类的父类是否继承了不被继承的类
如果这个类不是抽象类,是否实现了父类或接口中要求实现的所有方法
类中的字段、方法是否矛盾(例如覆盖了父类的final字段,或者方法重载不符合规则)
......

3.字节码验证

第三阶段将对类的方法体进行校验分析,保证方法是合法的、符合逻辑的,不会做出危害虚拟机的行为,要验证的方面有:

保证任意时刻操作数栈的数据类型和指令序列都能配合工作,例如不会出现这样的情况:对int型数据操作却用了对float型数据进行操作的指令
保证跳转指令不会跳转到方法体以外的字节码上
保证类型转换是正确的,例如可以把一个子类对象赋值给父类变量,但反过来不可以
......

4.符号引用验证

第四阶段可以看作是对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验,要验证的方面有:

根据类的全限定名字符串能否找到相应的类
在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段
符号引用中的类、字段、方法能否被当前类访问
......

三、准备

准备阶段是为类变量(静态变量)分配内存空间并设置变量初始值,这里的初始值是虚拟机内部设置的,并不是用户给变量赋予的值,这里也可以说是设置零值,例如int型变量的零值是0,float、double型变量的零值是0.0,更多类型的零值如下表:


JAVA中的零值

假如变量是final的,并且是基本数据类型或者String类型,那在准备阶段就会设置为被显式赋予的值。

四、解析

解析是把符号引用替换为直接引用的过程。
符号引用:通俗的讲,符号引用可以说是一些字符串,比如com.example.ClassA中使用了com.example.ClassB,那么ClassA的字节码会出现com/example/ClassB这样的字符串(符号)来说明引用了com.example.ClassB。
直接引用:直接引用可以是

  • 指向目标的指针(比如:指向类型[Class对象]、指向类变量、指向类方法的直接引用可能是指向方法区的指针)
  • 相对偏移量(比如指向实例变量、实例方法的直接引用都是偏移量)
  • 一个能间接定位到目标的句柄
1.类或接口的解析
  • 解析的类不是数组
    假如当前类是A,要解析类B,虚拟机会把类B的全限定名传递给加载类A的类加载器去加载它。
  • 解析的类是数组
    假如当前类是A,要解析类B的数组,会像上面那样加载类B,最后生成一个代表此数组维度和元素的对象。
  • 确保有访问权限
    最后进行符号引用验证,确认有解析的类的访问权限。
2.字段解析

对字段的解析,要先解析该字段所在的类或接口(假设该类为C),再按下面步骤搜索字段:
① 如果C包含了简单名称和描述符都符合的字段,则返回这个字段的直接引用。
② 否则,查找C实现的接口(递归搜索该接口的父接口),如果有简单名称和描述符都符合的字段,则返回这个字段的直接引用。
③ 否则,如果C不是java.lang.Object,则查找C的父类,如果有简单名称和描述符都符合的字段,则返回这个字段的直接引用。
④ 否则,查找失败,抛出java.lang.NoSuchFieldException异常。

查找到匹配的字段时,要检查是否有该字段的访问权限,如果没有,则抛出java.lang.IllegalAccessError异常。

3.类方法解析

对类方法的解析,也是要先解析该方法所在的类(假设该类为C),再按下面步骤搜索方法:
① 首先检查C是不是一个类,如果发现类C是个接口,会抛出java.lang.IncompatibleClassChangeError异常。
② 如果C中有简单名称和描述符都符合的方法,则返回此方法的直接引用。
③ 否则,如果C不是java.lang.Object,则递归查找C的父类,如果有简单名称和描述符都符合的方法,则返回这个方法的直接引用。
④否则,查找C实现的接口(递归搜索该接口的父接口),如果有简单名称和描述符都符合的方法,说明该方法是抽象方法,C是抽象类,抛出java.lang.AbstractMethodError异常。
⑤ 否则,查找失败,抛出java.lang.NoSuchMethodError异常。

查找到匹配的方法时,要检查是否有该方法的访问权限,如果没有,则抛出java.lang.IllegalAccessError异常。

4.接口方法解析

对接口方法的解析,也是要先解析该方法所在的接口(假设接口为I),再按下面步骤搜索方法:
① 先检查I是不是一个接口,如果I是一个类,会抛出java.lang.IncompatibleClassChangeError异常。
② 否则,如果I中有简单名称和描述符都符合的方法,则返回此方法的直接引用。
③ 否则,递归查找I的父接口和java.lang.Object,如果有简单名称和描述符都符合的方法,则返回此方法的直接引用。
④ 否则,宣告查找失败,抛出java.lang.NoSuchMethodError异常。

五、初始化

初始化的过程就是执行类构造器<clinit>的过程(如:一些静态变量的赋值语句、静态代码块)。
一些要注意的点:

  • 并非所有的类或接口都有<clinit>方法,如果类没有静态变量赋值和静态代码块、接口没有静态变量赋值,那么编译器可以不为它们生成<clinit>方法。
  • 执行类的<clinit>方法不会执行它实现接口的<clinit>方法(除非用到了接口的静态变量),执行接口的<clinit>方法不会执行父接口的<clinit>方法(除非用到了父接口的静态变量)。

参考资料:

《深入理解JAVA虚拟机》-周志明

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • 1.虚拟机如何加载这些Class文件?(类加载的过程)2.Class文件中的信息进入到虚拟机后会发生什么变化? J...
    wangcanfeng阅读 231评论 0 0
  • 概述 虚拟机把描述类的数据从Class文件加载到内存中,并对数据进行验证,准备,解析,初始化的一个过程,最终是可以...
    Wen_Q_M阅读 280评论 0 1
  • 基于JVM的语言,如java,kotlin,groovy等语言,在各自编译器编译完成之后,都会编译为.class文...
    Vinctor阅读 1,062评论 2 5
  • 注:此文是我在读完周志明老师的深入理解Java虚拟机之后总结的一篇文章,请阅读此书获取更加详细的信息. 类加载的时...
    AlstonWilliams阅读 492评论 0 0
  • 来自辜鸿铭《论语》 1.1 子曰:“学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不慍,不亦君子乎?” 辜...
    张coco阅读 432评论 0 0