内容简介:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,就是虚拟机的类加载机制。一个类从加载进内存到卸载出内存,经历图示的7个阶段:加载——>验证——>准备——>解析——>初始化——>使用——>卸载。
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,就是虚拟机的类加载机制。
类加载时机
类的生命周期
一个类从加载进内存到卸载出内存,经历图示的7个阶段:
加载——>验证——>准备——>解析——>初始化——>使用——>卸载。
其中,类的加载包括5个阶段:加载——>验证——>准备——>解析——>初始化。
在类加载的过程中,以下三个过程称为连接:验证——>准备——>解析。
因此JVM的类加载过程也可以概括为:加载——>连接——>初始化。
C/C++在运行前需要完成预处理、编译、汇编、链接;在Java中,类加载(加载、连接、初始化)是在程序运行期间完成的,这种策略虽然会使类加载时稍微增加一些性能开销,但是会为java应用程序提供高度的灵活性。java可以动态扩展语言特性就是依赖运行期间动态加载和动态链接这个特点实现的。比如,如果编写一个面向接口的程序,可以等到运行时再指定其具体实现类。
加载、验证、准备、初始化和卸载这五个阶段的顺序确定,而解析阶段不一定,在某些情况下可以在初始化阶段之后再开始。这是为了支持Java语言的运行时绑定。
类的加载时机
虚拟机并没有严格约束什么时候进行类加载,但是对于初始化的时机有严格的规定。在初始化之前,加载、验证、准备都应该开始。
初始化开始的时机:
- 遇到new,getstatic,putstatic,invokestatic四个字节码指令,如果类没有进行初始化,则需要先触发其初始化。
- 使用new关键字实例化对象的时候
- 读取或设置一个类的静态字段(不包括final以及编译期放入常量池的静态字段)
- 调用一个类的静态方法
- 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。
- 初始化一个类的时候,若其父类未初始化,先触发父类初始化,然后初始化本类。
- 当虚拟机启动时,用户指定一个需要执行的主类(main()方法的类),虚拟机会先初始化这个主类。
- 使用JDK1.7动态语言支持的时候的一些情况。
有且只有上述五种场景才会触发类进行初始化,称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。 以下为被动引用的几个例子
- 通过子类引用父类的静态字段,不会导致子类初始化
public class SuperClass{ static{ System.out.println("SuperClass init!"); } public static int value=123; } public class SubClass extends SuperClass{ static{ System.out.println("SubClass init!"); } } public class InitTest1{ public static void main(String[] args){ System.out.println(SubClass.value); } } 复制代码
上述代码运行会输出:"SuperClass init!"以及123。对于静态字段,只有直接定义这个字段的类才会被初始化。 2. 通过数字定义引用类,不会触发此类的初始化
public class InitTest2{ public static void main(String[] args){ SuperClass[] sca=new SuperClass[10]; } } 复制代码
上述代码没有任何输出。 3. 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用定义常量的类,因此不会触发定义常量的类的初始化
class ConstClass{ static{ System.out.println("ConstClass init"); } public static final String HELLOWORLD="hello world"; } public class InitTest3 { public static void main(String[] args){ System.out.println(ConstClass.HELLOWORLD); } } 复制代码
上述代码只输出了“hello world”。原因是在编译阶段,已经将常量的值存储到了InitTest3类的常量池中,在该类中对ConstClass.HELLOWORLD的引用实际上被转化为InitTest3类自身常量池的引用。
接口的初始化
对于接口的初始化过程,与类的区别主要在于第三种情形,当一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口是,才会初始化。
参考资料
- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011.
- github.com/CyC2018/CS-…
- mp.weixin.qq.com/s?__biz=MzU…
- crossoverjie.top/JCSprout/#/…
以上所述就是小编给大家介绍的《虚拟机类加载机制:类加载时机》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- JVM类生命周期概述:加载时机与加载过程
- 了解 .NET/C# 程序集的加载时机,以便优化程序启动性能
- 封装中修改旧代码的时机
- 监听reloadData刷新列表完毕的时机
- 国内存储芯片迎来最好的发展时机
- 什么时候才是代码重构的最佳时机?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入应用C++11
祁宇 / 机械工业出版社 / 2015-5 / 79
在StackOverflow的最近一次世界性调查中,C++11在所有的编程语言中排名第二, C++11受到程序员的追捧是毫不意外的,因为它就像C++之父Bjarne Stroustrup说的:它看起来就像一门新的语言。C++11新增加了相当多的现代编程语言的特性,相比C++98/03,它在生产力、安全性、性能和易用性上都有了大幅提高。比如auto和decltype让我们从书写冗长的类型和繁琐的类型......一起来看看 《深入应用C++11》 这本书的介绍吧!