虚拟机把描述类的数据从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种场景的行为称为对一个类的“主动引用”,除此之外所有引用类的方式都不会触发初始化,不触发初始化的也被称为被动引用

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

  1. /**
  2. * 通过子类引用父类的的静态字段,不会导致子类初始化
  3. */
  4. public class SuperClass {
  5.  
  6. static {
  7. System.out.println("SuperClass init!");
  8. }
  9.  
  10. public static int value = 888;
  11.  
  12. public static final String JVM_TEST = "JVM TEST";
  13. }
  14.  
  15. /**
  16. * 子类
  17. */
  18. public class SubClass extends SuperClass {
  19.  
  20. static {
  21. System.out.println("SubClass init!");
  22. }
  23.  
  24. }
  25.  
  26. /**
  27. * 测试
  28. */
  29. public class Test {
  30.  
  31. public static void main(String[] args){
  32. System.out.println(SubClass.value);
  33. }
  34. }

打印结果为:

SuperClass init!
888

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

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

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

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

运行结果也没有打印出“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. javax.mail

    摘抄 example: public static void sendEmail(ConfBean cBean, String filename, String filepath) { try { P ...

  2. 解决miner.start() 返回null

    下面我列举了三种解决方案,前面两张是某两位大佬试出来的,第三种是我蒙出来的. 第一种解决方案 转载自  http://mp.weixin.qq.com/s/AB9vj1EzOgWT7JyryfQhDg ...

  3. 学会分析YUV数据

    做视频采集与处理,自然少不了要学会分析YUV数据.因为从采集的角度来说,一般的视频采集芯片输出的码流一般都是YUV数据流的形式,而从视频处理(例如H.264.MPEG视频编解码)的角度来说,也是在原始 ...

  4. Django之代码风格

    1 代码风格 稍微关注一下下面这些代码标准风格指导规则将会对你大有益处,我们高度建议你通读词章,即便你此时可能正想跳过它. 1.1 让你的代码保持可读性的重要性 代码在读方面的重要性胜过写.一个代码块 ...

  5. git添加本地的项目到git远程管理仓库

    目标:将本地存在的项目添加到git远程仓库管理 步骤: 1. 需要一个git远程仓库管理地址 例如:https://github.com/xingfupeng/test.git git@github. ...

  6. Flask类的属性和方法大全

    Flask Property__class____dict____doc____module__app_ctx_globals_classconfig_classdebugdefault_config ...

  7. 使用webpack打包后的vue项目如何运行(express)

    我们知道使用webpack打包vue项目后会生成一个dist文件夹,dist文件夹下有html文件和其他css.js以及图片等,那么打包后的文件该如何正确运行呢? 倘若直接打开html文件,会报如下错 ...

  8. 使用on-my-zsh时,php 输出内容后面多个%号

    今天用php写个命令行的小工具时,突然发现在echo输出后,总是会多个%号,开始以为是代码的问题,然后新建了一个代码文件: <?php echo 'hello world'; 输出结果: hel ...

  9. Java 面试知识点解析(三)——JVM篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  10. 用nginx缓存静态文件

        这篇教程说明你应该怎样配置 nginx.设置 HTTP 头部过期时间,用 Cache-Control 中的 max-age 标记为静态文件(比如图片. CSS 和 Javascript 文件) ...