文档章节

类加载机制

i
 itititit
发布于 2017/08/22 10:50
字数 1394
阅读 13
收藏 0

类加载时机

类的整个生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。

什么情况下需要开始类加载过程的第一步:加载?jvm并没有进行强制的规定。但对于初始化阶段,jvm严格规定了只有5种情况必须立即对类进行初始化(而加载、验证、准备和解析自然需要在此之前):

  1. 遇到new、getstatic、putstatic或invokestatic这四条字节码指令时。生成这4条指令码常见的场景为:使用new实例化对象时、设置或读取一个类的静态变量(被final修饰的除外),调用类的静态方法。
  2. 使用java.lang.reflect包中的方法对类进行反射调用的时候。
  3. 当初始化一个类时,其父类还未初始化。
  4. 虚拟机启动时,需要指定要执行的主类(包含main()方法的类),jvm会先初始化这个类。
  5. 当使用jdk1.7的动态语言时,如果一个java.lang.invoke.MethodHandle实例最后的解析过程结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄时

这5中场景称为主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。

  1. 通过子类引用父类的静态字段。
  2. 通过数组定义引用类,如Klass c = new Klass[10] 。但是会触发另外一个名为[L...Klass的类的初始化。
  3. 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类。

对于接口与类的加载有所不同,在于前述5种中的第三种:对于接口初始化时,并不要求其父接口完成初始化,只有在真正用到的时候才会初始化(如引用接口中定义的常量)。

 

 

类加载的过程

加载

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

 

验证

  • 文件格式的验证
    • 0xCAFEBABE
    • 主、次版本号
    • 常量池中是否存在不支持的常量(检查常量flag标志)
    • 指向常量池的索引值是否存在
    • CONSTANT_UTF8_info常量是否存在不符合UTF8编码的数据
    • Class文件中各部分及文件本身是否有被删除的或附加的其他信息

...

  • 元数据验证

    • 是否有父类
    • 是否继承不允许继承的类
    • 如果不是抽象类,是否实现了父类或接口中的所有方法
    • 类中的字段、方法是否与父类发生矛盾
  • 字节码验证
    • 确定程序语义合法、符合逻辑。
    • 保证指令不会跳出方法体外
    • 保证方法体中的类型转换有效
  • 符号引用验证
    • 将符号引用转换为直接引用,这个转化过程发生在解析过程。
    • 符号引用是否能找到对应的类
    • 符号引用的类、字段、方法的访问性是否可被当前类访问
    • 验证阶段不是必须的,如果全部代码经过反复的使用和验证,可通过-Xverify:none参数来关闭大部分的类验证措施。

准备

正式为类变量分配内存并设置类变量初始值的阶段(零值),这些变量所使用的内存都将在方法区分配(仅包括类变量static)。但是特殊情况下,如类字段的字段属性表存在ConstantValue属性,那么在准备阶段会被初始化为ConstantValue属性指定的值。public static final int value = 123; 

解析

将符号引用替换为直接引用的过程。

  • 类的解析:把一个从未解析过的符号引用解析为一个类或接口C的直接引用,可能涉及C相关联的加载动作。
  • 字段解析:先从C中查找,然后是接口及其接口的父接口,按照继承关系从下往上查找父类,否则抛出异常。查找成功后验证权限。
  • 类方法:符号引用的类中的方法,父类的方法中查找。如果是接口或抽象方法则抛出异常。
  • 接口方法:

初始化

该阶段是执行类构造器<clinit>() 方法的过程。

<clinit>()方法有编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的。收集的顺序是按语句在源文件中出现的顺序决定,静态语句只能访问定义在静态语句块之前的变量,定义在它后面的变量,只能赋值,不能访问。

<clinit>()与实例构造器<init>()方法不同,它不需要显示地调用父类的<clinit>(),jvm会保证子类<clinit>()方法调用前,调用父类的<clinit>()方法已经执行完毕。

<clinit>()并不是必须的。

接口中同样存在<clinit>()方法,但是不需要先执行父接口的<clinit>()。

jvm会保证一个类的<clinit>()方法在多线程环境中被正确的加锁、同步。执行<clinit>()方法的线程退出后,其他阻塞的线程不会再次进入<clinit>()方法。

 

 

类加载器

 

 

 

 

 

 

 

© 著作权归作者所有

i
粉丝 0
博文 24
码字总数 15531
作品 0
私信 提问

暂无文章

springboot全家桶,集成shiro,rabbitmq,redis, mybatis,druid,swagger

搭建了一个SpringBoot的模板,下载即可作为单体应用的服务后台模板 项目地址:https://gitee.com/devilscode/springboot_template 项目集成框架 springboot 作为基础框架 shiro 权限管理 ra...

devils_os
23分钟前
5
0
云服务器的优势有哪些?

与传统的服务器相比,云服务器有多种显著的优势,因此近年来云服务器租用一直都很受消费者的欢迎。 那么云服务器都有哪些优势呢? 云服务器的稳定性很好 虚拟主机是很多用户建站的首选方案,这...

云漫网络Ruan
23分钟前
3
0
好程序员web前端学习路线分享CSS浮动-清除浮动篇

好程序员web前端学习路线分享CSS浮动-清除浮动篇,为什么要清除浮动   这里所说的清除浮动,并不是不要浮动了,而是清除浮动与浮动之间的影响。那么到底会有什么影响呢? 1.高度塌陷   ...

好程序员官方
34分钟前
3
0
我有酒,你有故事吗?

记录学习的每一秒,巨杉技术社区有奖征文全新启动! 如果, 你热爱数据技术,乐于尝(zhe)试(teng), 又喜欢写点什么~ 那么, 无论你是架构大佬,还是DBA大神, 只要你愿意通过文字分享你...

巨杉数据库
38分钟前
3
0
【Git】ignore文件不生效

简介 使用git的过程中, 某些文件可能不希望上传到服务器,例如日志、本地化配置等信息。这时候,我们可以通过配置.gitignore文件忽略这些文件的提交。 配置.gitignore 常见的配置如下: HELP...

Areya
41分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部