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

在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的。类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:

加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中哦验证、准备、解析3个部分统称为连接(Linking),这7个阶段顺序如下图:

其中加载、验证、准备、初始化、和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始(这里仅仅指的是开始,而不是按部就班的进行完成,是因为这些阶段通常都是相互交叉的进行的,通常在一个阶段执行的过程中调用、激活另外一个阶段),而解析阶段则不一定,它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定。

那么在什么时候开始类加载过程的第一个阶段(也就是加载)呢?Java虚拟机规范中并没有进行强制约束,这点虚拟机根据自身实现来把握。但对于初始化阶段,虚拟机规范则是严格规定了有且只有5中情况必须立即对类进行初始化(加载,验证,准备肯定要在此之前进行了)。

  1. 创建类实例的时候,读取或者设置一个类的静态字段(被final修饰,已在编译期把结果放入常量池的除外),以及调用一个类的静态方法的时候。
  2. 对类进行反射调用的时候,如果没有进行过初始化则需要先出发其初始化过程。
  3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先出发其父类的初始化过程。
  4. 当虚拟机启动时,定义了入口(含有main()方法的那个类)的主类,虚拟机会先初始化这个主类。
  5. 当使用JDK1.7及以上的版本中的动态语言支持时,若一个java.lang.invoke.MethodHandle实例最后的解析结果是:REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先出发它的初始化过程。

虚拟机规范中指出有且只有这5种场景会出发初始化,并且这5种场景的行为称为对一个类的“主动引用”,除此之外所有引用类的方式都不会触发初始化,不触发初始化的也被称为被动引用

用代码例子来说明被动引用。

/**
* 通过子类引用父类的的静态字段,不会导致子类初始化
*/
public class SuperClass { static {
System.out.println("SuperClass init!");
} public static int value = 888; public static final String JVM_TEST = "JVM TEST";
} /**
* 子类
*/
public class SubClass extends SuperClass { static {
System.out.println("SubClass init!");
} } /**
* 测试
*/
public class Test { public static void main(String[] args){
System.out.println(SubClass.value);
}
}

打印结果为:

SuperClass init!
888

对于静态字段,只有定义这个字段的类才会被初始化,因此通过子类调用其父类中定义的静态字段,只会出发父类的初始化。

/**
* 通过数组定义引用类,不会出发类的初始化
*/
public class Test { public static void main(String[] args){
SuperClass[] supers = new SuperClass[12];
}
}

运行结果并没有打印出“SuperClass init!”,这说明并没有对SuperClass进行初始化,定义数组不会触发类的初始化

/**
* 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类中,因此不会出发定义常量的类的初始化。
*/
public class Test { public static void main(String[] args){
System.out.println(SuperClass.JVM_TEST);
}
}

运行结果也没有打印出“SuperClass init!”,因为虽然引用了SuperClass的常量,但其实在编译极端通过常量传播优化,已经将此常量存储到了Test类的常量池中,因Test类对此常量的引用,都会转化为Test类对自身常量池的引用了。这说明SuperClass和Test这两个类,在编译阶段完成后就没有任何关系了。

接口的加载过程和类的加载过程步骤上是一致的,但是稍有不同的是上面的例子都是用静态语句块“static{}”来输出初始化信息的,在接口中不能使用“static{}”静态语句块。还有一个不同是:当一个类在初始化的时候,要求其父类全部都已经初始化过了,但是一个接口在初始化的时候,不要求其父接口都初始化过,只有真正使用到父接口的时候(例如:引用父接口中定义的常量)才会初始化。

JVM学习记录-类加载时机的更多相关文章

  1. JVM学习记录-类加载的过程

    类的整个生命周期的7个阶段是:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Us ...

  2. JVM学习记录-类加载器

    前言 JVM设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外面去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类 ...

  3. JVM学习笔记——类加载和字节码技术篇

    JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...

  4. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  5. JVM学习笔记——类加载过程

    JVM学习笔记——类加载过程 类加载模型——双亲委派模型(Parents Delegation Model)也可称为“溯源委派加载模型” Java的类加载器是一个运行时核心基础设施模块,主要是启动之初 ...

  6. JVM学习记录-线程安全与锁优化(二)

    前言 高效并发是程序员们写代码时一直所追求的,HotSpot虚拟机开发团队也为此付出了很多努力,为了在线程之间更高效地共享数据,以及解决竞争问题,HotSpot开发团队做出了各种锁的优化技术常见的有: ...

  7. JVM学习之类加载

    该文使用Hotspot    JDK1.7 一.类加载器 1.什么是类加载器 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java ...

  8. JVM学习记录

    本博客是为了自己学习JVM而建立,只记录一些自己学习的经过. 最近在看<深入理解Java虚拟机>这本书,里面的内容,很是乏味,因为看不懂所以就会觉得很枯燥,觉得很枯燥看着看着就犯困,然后就 ...

  9. jvm学习记录-对象的创建、对象的内存布局、对象的访问定位

    简述 今天继续写<深入理解java虚拟机>的对象创建的理解.这次和上次隔的时间有些长,是因为有些东西确实不好理解,就查阅各种资料,然后弄明白了才来做记录. (此文中所阐述的内容都是以Hot ...

随机推荐

  1. SQL语言逻辑执行顺序

    SQL语言逻辑执行顺序 2012-12-18 16:18:13 分类: 数据库开发技术 查询的逻辑执行顺序 FROM < left_table> ON < join_conditio ...

  2. Docker 三剑客之 Docker Swarm(基于 overlay 组网通信)

    相关文章:Docker 三剑客之 Docker Swarm 这一篇主要是对 Docker Swarm 的完善,增加基于 overlay 组网通信,以便 Docker 容器可以跨主机访问. 不同主机间的 ...

  3. (汇总)os模块以及shutil模块对文件的操作

    ''' # os 模块 os.sep 可以取代操作系统特定的路径分隔符.windows下为 '\\' os.name 字符串指示你正在使用的平台.比如对于Windows,它是'nt',而对于Linux ...

  4. go redigo的简单操作

    golang操作redis主要有两个库,go-redis和redigo.两者操作都比较简单,区别上redigo更像一个client执行各种操作都是通过Do函数去做的,redis-go对函数的封装更好, ...

  5. JavaScript的数组知识案例之随机点名器

    本次分享JavaScript主要知识点涉及到for循环.if选择结构判断语句.数组的定义.定时器.清除定时器.日期对象的使用. 执行后效果图: 思路: 1.网页结构搭建: HTML 2.网页布局美化: ...

  6. 关于eclipse的一些问题

    解决Eclipse,MyEclipse出现An error has occurred,See error log for more details的错误 方法1. 在"开始"--& ...

  7. java的Calendar,获取月份少一月的问题及其它注意事项

    java的Calendar,获取月份少一月的问题及其它注意事项: import java.util.*; public class DateClass{ public static void main ...

  8. meta 元标签的常用用法

    < meta > 元素 概要 标签提供关于HTML文档的元数据.元数据不会显示在页面上,但是对于机器是可读的.它可用于浏览器(如何显示内容或重新加载页面),搜索引擎(关键词),或其他 we ...

  9. IDEA无法创建类,接口

    原因:模板丢失 解决方案: 在idea.exe.vmoptions 或 idea64.exe.vmoptions中加入配置-Djdk.util.zip.ensureTrailingSlash=fals ...

  10. 使用SimHash进行海量文本去重[转载]

    阅读目录 1. SimHash与传统hash函数的区别 2. SimHash算法思想 3. SimHash流程实现 4. SimHash签名距离计算 5. SimHash存储和索引 6. SimHas ...