虚拟机并没有要求什么时候进行其他阶段的工作,但初始化阶段不同。当发生一下几种情况时,虚拟机必须要开始初始化工作。(作为初始化前的加载,验证,准备,解析也就都按部就班开始了)。
当在字节码层面遇到以下指令时,new(对象都要生成了,肯定要初始化了),get/put static(使用静态变量了,肯定要赋值了),invoke static(调用静态方法了都,肯定要为静态量赋值);
反射调用。当使用java。lang。reflect中的方法对类进行反射调用;
初始化一个类的时候,发现父类还有初始化,那么需要先初始化其父类,(父接口不用立即初始化,只有使用到其常量时,才需要将其初始化);
虚拟机需要一个入口,因此主类需要初始化;
动态方法解析,解析出方法是其他类的静态方法,那么需要将其初始化。
虚拟机规定有且仅有以上5种方法需要立即初始化,还有一些调用,看起来像需要初始化,但其实并不需要,可以称之为被动调用。
子类直接使用父类的静态变量。虚拟机规定只有直接定义静态变量的类需要初始化,因此这种情况下,只会触发父类的初始化,而子类并不会触发;
数组对象。当定义对象数组时,只会触发数组类的初始化,其内的对象的类并不会初始化;
当一个类调用另一个类的常量时。此时并不会对常量类(被调用者)进行出初始化。只会将调用者初始化。因为在编译阶段,根据常量传播优化,会将常量类的常量放置到调用者的常量池中,此时这两个类已经没有了瓜葛,因此也就不存在将其初始化了。
总结在本文中着重介绍了一个类加载入内存中的各个阶段过程,了解这个阶段过程可以明白虚拟机是如何将一个静态的类文件,经过一系列的动作变为 Java 内存中的各种数据结构。
在下一篇文章我们将会介绍执行加载阶段的主体,类加载器,明白类加载器的模型以及其背后的逻辑,并尝试自定义一个类加载器,来完成加载工作。
文章在公众号 "iceWang" 第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!
本系列文章主要借鉴自《深入分析 JavaWeb 技术内幕》和《深入理解 Java 虚拟机- JVM 高级特性与最佳实践》。