全面解析JVM加载中初始化的时机

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 全面解析JVM加载中初始化的时机

JVM类加载过程

JVM类加载过程分为几个阶段,分别是加载验证准备解析初始化加载是把二进制字节码载入内存,验证是校验字节流中包含的信息是否符合当要求,准备是为静态变量分配内存并设置静态变量初始值,解析是把常量池内的符号引用替换为直接引用,初始化是执行所有静态变量的赋值动作和静态语句块中的语句。更多详尽分析请阅读之前的文章《JVM的类加载机制全面解析》,这里不再赘述了。

类初始化的时机

对于我们开发人员,我认为应该具体了解一下初始化阶段什么时候在开始。JVM规范对此做了严格规范,有且只有以下5种情况必须对类进行初始化:

  1. 遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类没有被初始化过,就需要先进行初始化。对于字节码指令不了解的同学,可能就是一脸蒙圈了。我们来说人话,就是:使用new关键字实例化对象的时候、读取和设置一个类的静态字段(不被final修饰的)和调用一个类的静态方法的时候。这样说更容易被理解一些。
  2. 使用java.lang.reflect包中的方法对类进行反射调用的时候,如果类没有被初始化过,就需要先进行初始化。
  3. 当初始化一个类的时候,如果发现它的父类还没有被初始化过,就需要先初始化它的父类。
  4. JVM会先初始化要执行的主类,也是包含main()方法的那个类。
  5. 当使用JDK 1.7的动态语言支持时,如果java.lang.invoke.MethodHandle实例最后的解析结果是REF_getStatic(使用MethodHandle读取类的静态字段)、REF_putStatic(使用MethodHandle设置类的静态字段)、REF_invokeStatic(使用MethodHandle调用类的静态方法)的方法句柄时,如果这个方法句柄没有被初始化过,就需要先进行初始化。

被动引用

刚刚提到的5种情况,都会触发初始化,这些行为为称为对一个类的主动引用。除了这些以外,所有引用类的方式都不会触发初始化,被为被动引用。为了更好的理解,下面举几个被动引用的例子。

通过子类引用父类的静态变量
public class SuperClass {
    static {
        System.out.println("父类正在初始化");
    }
 
    public static String name = "万猫学社";
}
public class SubClass extends SuperClass {
    static {
        System.out.println("子类正在初始化");
    }
}
public class OneMoreStudy {
    public static void main(String[] args) {
        System.out.println(SubClass.name);
    }
}

对于静态变量,只有直接定义这个变量的类才会被初始化,通过子类引用父类中定义的静态变量,只会触发父类的初始化而不会触发子类的初始化,运行的结果是:

父类正在初始化
万猫学社

结果中并没有“子类正在初始化”。

通过数组定义来引用类
public class OneMoreStudy {
    public static void main(String[] args) {
        SuperClass[] arrays = new SuperClass[10];
        System.out.println("数组元素个数:" + arrays.length);
    }
}

这段代码中使用之前的SuperClass类,定义了一个SuperClass类的一维数组,运行后的结果是:

数组元素个数:10

结果中并没有“父类正在初始化”,说明并没有触发SuperClass类的初始化。实际上,有一个名为“[LSuperClass”的类被初始化了,它是由JVM自动生成的、直接继承于java.lang.Object,创建动作由字节码指令newarray触发。

常量
public class ConstClass {
    static {
        System.out.println("有常量的类正在初始化");
    }
 
    public static final String NAME = "万猫学社";
}
public class OneMoreStudy {
    public static void main(String[] args) {
        System.out.println(ConstClass.NAME);
    }
}

常量在编译阶段会存入调用类的常量池中,本质没有直接引用到定义的常量的类,不会触发定义常量的类的初始化,所以运行的结果是:

万猫学社

结果中并没有“有常量的类正在初始化”。

接口初始化的时机

接口也有初始化过程,和类是一致的。不过接口中不能使用“static{}”语句块,但编译器仍然会为接口生成“clinit()”类构造器,用于初始化接口中所定义的成员变量。

接口初始化的时机,基本和之前提到的类的5种情况基本一致,唯一不一样的是第3种情况:在一个类被初始化时,它的父类也必须被初始化,但是一个接口被初始化时,它的父接口并不要求被初始化。只有在真正使用到父接口时才会被初始化,比如:引用父接口中定义的常量。

结语

这次主要分享了类在什么时候被初始化,共有5种情况。除了这种5种情况的引用叫做被动引用,同时举了3个被动引用的例子。同时,也提到初始化接口和类有什么不同。

相关文章
|
1月前
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
12天前
|
存储 Java 开发者
浅析JVM方法解析、创建和链接
上一篇文章《你知道Java类是如何被加载的吗?》分析了HotSpot是如何加载Java类的,本文再来分析下Hotspot又是如何解析、创建和链接类方法的。
|
21天前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
1天前
|
存储 安全 Java
JVM加载过程
JVM类加载过程是Java开发中的关键环节,主要包括五个阶段:加载、验证、准备、解析和初始化。加载阶段获取类的二进制字节流;验证确保字节码符合规范;准备为静态变量分配内存并默认初始化;解析将符号引用转为直接引用;初始化执行静态变量赋值和静态代码块。了解这一过程有助于深入理解Java程序运行机制,提升编程水平。
|
25天前
|
Java 编译器 API
深入解析:JDK与JVM的区别及联系
在Java开发和运行环境中,JDK(Java Development Kit)和JVM(Java Virtual Machine)是两个核心概念,它们在Java程序的开发、编译和运行过程中扮演着不同的角色。本文将深入解析JDK与JVM的区别及其内在联系,为Java开发者提供清晰的技术干货。
24 1
|
2月前
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
36 3
|
2月前
|
存储 安全 Java
JVM锁的膨胀过程与锁内存变化解析
在Java虚拟机(JVM)中,锁机制是确保多线程环境下数据一致性和线程安全的重要手段。随着线程对共享资源的竞争程度不同,JVM中的锁会经历从低级到高级的膨胀过程,以适应不同的并发场景。本文将深入探讨JVM锁的膨胀过程,以及锁在内存中的变化。
47 1
|
2月前
|
存储 编译器 数据安全/隐私保护
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解2
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
42 3
|
2月前
|
Python
深入解析 Python 中的对象创建与初始化:__new__ 与 __init__ 方法
深入解析 Python 中的对象创建与初始化:__new__ 与 __init__ 方法
23 1
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2

推荐镜像

更多