Java程序运行于Java虚拟机之上,JVM屏蔽了底层细节,使得Java程序能够“一次编译,到处运行”。在Java语言中,一切皆是对象,代码一般由类、接口、enum等构成,是一种面向对象的编程语言。本文将为你揭示Java虚拟机如何加载类,一窥Java底层的秘密。
类在虚拟机中的生命周期,可以分为加载、验证、准备、解析、初始化、使用、卸载几个阶段,其中的验证、准备、解析统称为连接。在这里,读者可以回忆一下以C语言为代表的面向过程语言如何实现动态链接库,以更好地理解Java面向对象编程。
通常情况下,虚拟机都会按照上图流程管理类的生命周期。然而,Java语言的一大特性——多态支持方法的动态绑定,即,调用方法前无法知道具体调用了那个方法,只有运行到调用的时刻才能确定方法的具体实现。因此,解析也可能发生在初始化之后,在多态调用时才解析出具体的直接引用。
加载
在Java虚拟机规范中,并没有强制要求什么时候加载类,由虚拟机自行把握。在加载阶段,虚拟机通过一个类的全限定名获取类的二级制字节流,把字节流的静态存储结构转换为运行时数据结构,在内存中生成一个Class对象,Class对象将作为方法区的访问入口。
在Java中,能够根据全限定名获取字节流的代码块被称为类加载器。主要包括启动类加载器、扩展类加载器、应用程序类加载器和用户自定义类加载器。其中,
启动类加载器加载jre的lib目录下的类,如rt.jar,在Hotspot虚拟机中用c++实现,是虚拟机的一部分;
扩展类加载器加载jre的lib/ext或者由系统变量 java.ext.dir指定目录中的类,一般Java语言实现;
应用程序类加载器加载CLASSPATH中的类,一般Java语言实现;
自定义类加载器用于程序实现个性化的类加载,如spring提供的ClassLoader、用于热升级的ClassLoader、从网络加载jar包的ClassLoader。
在加载类的过程中,Java采用了双亲委派机制。而这种父子关系并不是通过继承实现的,而是组合关系。一个类加载器需要加载类时,首先委托父类加载器进行加载,并逐级向上,如果父类加载器加载成功则返回成功,如果父类加载器加载失败,则自己进行加载。在Java中,类的唯一性是由类和所属的类加载器共同确定的。两个类加载器加载的同一个class,在虚拟机看来也是不同的类。通过双亲委派机制,Java能够保证核心类不会被用户覆盖,因用户企图覆盖核心类时类加载器总能找到已由父类加载器加载的核心类。
验证
只要符合class文件的格式要求的class文件都能被虚拟机加载,不管class文件是不是由Java编译器所产生。Java虚拟机出于自身安全的考虑,会对加载的类进行合法性验证。
在验证阶段,虚拟机将进行文件格式验证、元数据验证、字节码验证和符号引用验证。此阶段主要的目标是确保Class文件的字节流包含的信息符合虚拟机的要求。
文件格式验证:验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。如是否以魔数0xCAFEBABE开头,主、次版本号是否在当前虚拟机处理范围之内等。
元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求,如是否有父类,父类是否继承了不允许被继承的类,类中的字段、方法是否与父类产生矛盾等。
字节码验证:对类的方法体进行校验分析,保证被校验类的方法在运行时不会做出危害虚拟机安全的事件。如保证跳转指令不会跳转到方法体以外的字节码指令上。
符号引用验证:对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验,如符号引用中通过字符串描述的全限定名是否能找到对应的类,符号引用中的类、字段、方法的访问性(private、protected、public、default)是否可被当前类访问等。
准备
在准备阶段,虚拟机将为类变量在方法区分配内存并设置类变量的初始值。此时的初始值并不是源代码中的初始值,而是各种类型变量的默认初始值,如int类型为0、boolean类型为false。源代码中的变量初始值,会在<clinit>方法中赋值,在初始化阶段完成。
解析
在解析阶段,虚拟机将常量池内的符号引用替换为直接引用。在类初始化之前,解析操作只能解析静态绑定的符号引用。
符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。
直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是和虚拟机实现的内存布局相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经在内存中存在。
初始化
虚拟机规范要求有且只有以下几种情况触发类的初始化,也就是说连接之后的类并没有立即执行初始化,而是在使用前才进行初始化:
①使用new创建对象、读写类的静态字段、调用类的静态方法时需要进行初始化,但final修饰的字段除外。读写静态字段时,只有静态字段所在的类会被初始化。
②使用反射调用的时候,如果类没有初始化则先初始化。
③初始化一个类的时候,如果它的父类还没有初始化,则先触发其父类的初始化。
④虚拟机启动时,用于执行的包含main方法的类需要先初始化;
⑤使用动态语言支持时,如果解析结果引用的类没有进行初始化,则需要先初始化。
在编译阶段,编译器会扫描源文件,根据类中的变量赋值和静态语句块生成<clinit>方法,并在初始化阶段执行<clinit>方法。
<clinit>方法中初始化过程与源代码中语句顺序保持一致,静态语句块只能访问之前的变量,对于之后的变量只能赋值不能访问。如果类中没有变量赋值和静态语句块,则不会生成<clinit>方法。在讲解继承的时候,通常都会提到父类会先于子类进行初始化,一定程度上也是因为父类的<clinit>方法会先于子类执行。
如果接口定义了常量,也会生成<clinit>方法,与类不同的是,接口初始化时不需要先调用父接口的<clinit>方法,只有在用到父接口的变量时才执行父接口的<clinit>方法。并且,接口的实现类在初始化时也不会调用接口的<clinit>方法,因此方法属于接口不属于实现类。
在初始化过程中,虚拟机会保证多线程并发情况下类能够被正确初始化,即<clinit>方法会被虚拟机加锁和同步,同一时间只有一个线程能够执行<clinit>方法。
总结
虚拟机的类加载机制,分为加载、验证、准备、解析、初始化五个阶段。采用双亲委派机制从类的文件二进制流载入,然后进行类的合法性验证,分配类的运行时数据空间,对符号引用进行解析转为直接引用,最后执行类的初始化。
原文地址:Java虚拟机类加载机制
相关推荐
Java虚拟机类加载机制及双亲委派模型
经典的java虚拟机类加载机制 看完后会有醍醐灌顶的感觉
虚拟机将描述类的数据从Class文件加载到内存,并对数据进行校验、准备、解析和初始化,终会形成可以被虚拟机使用的Java类型,这是一个虚拟机的类加载机制。Java中的类是动态加载的,只有在运行期间使用到该类的...
在《Java虚拟机类加载机制》一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的后留了一个悬念给各位,这里来揭开这个悬念。建议先看完《Java虚拟机类加载机制》这篇再来看这个,印象会比较深刻...
虚拟机把描述类的数据从Class文件中加载到内存,并对数据进行校验、转换解析和初始化,最终形成可被虚拟机直接使用的Java类型,这就是虚拟机加载机制。
Java类加载机制的原理是基于Java虚拟机的架构,Java虚拟机将Java类文件编译生成的.class文件加载到内存中,并将其转换为机器可执行的代码。类加载机制的过程可以分为三个阶段:加载、链接和初始化。 加载阶段:在...
Java的类加载机制遵循双亲委派模型,即当一个类加载器收到加载类的请求时,它首先会委托父类加载器去尝试加载,只有当父类加载器无法加载时,当前类加载器才会尝试自己加载。这种模型保证了Java核心库类的一致性和...
Java 动态类加载机制是 Java 虚拟机(JVM)的一种机制,允许在 Java 应用程序运行期间动态加载类文件,而不影响其他功能模块的正常运行。这种机制可以提高 Java 应用程序的灵活性和可扩展性。 Java 动态类加载机制...
JVM解释执行机制是Java虚拟机执行字节码的一种方式,其中解释器将Java字节码逐条转换成机器码并执行。 Java语言的跨平台特性得益于其编译后不是直接生成特定平台上的机器码,而是生成一种中间形式的字节码。Java源...
Java 动态类加载机制研究及应用是基于 Java 虚拟机(JV M)机制的,旨在实现 Java 应用程序中动态加载类文件,而不影响其他功能模块的正常运行。为了实现这个目标,需要对 Java 类加载器的体系结构、动态类加载机制...
什么是虚拟机类加载机制以及加载过程,以及类加载时机
这份规范涵盖了虚拟机的结构、字节码编译、字节码文件格式、类加载机制以及虚拟机指令集等多个关键领域。 **虚拟机结构** Java虚拟机的结构包括内存区域、执行引擎、类加载器系统等组件。内存区域主要分为堆、栈、...
Java类加载机制是Java程序运行的第一步,它对于理解Java虚拟机(JVM)的行为至关重要。类加载过程涉及到类的加载、链接(验证、准备、解析)、初始化等阶段,并且这一过程是由类加载器系统完成的。 #### 二、类加载...
1. 字节码加载:Java虚拟机首先加载字节码文件,然后对其进行解析和验证。 2. 字节码验证:Java虚拟机对字节码进行验证,检查其是否符合Java虚拟机的规范。 3. 字节码执行:Java虚拟机执行字节码,通过解释或编译来...
Java虚拟机规范还定义了类加载机制,即在JVM启动时或运行过程中动态加载类到内存中。类加载器在运行Java程序时按需加载类,这允许Java程序进行延迟加载,即仅在需要时才加载类,这有助于优化程序的启动时间和运行时...
它详尽地探讨了Java虚拟机的工作原理、内存管理、类加载机制、字节码执行以及性能优化等多个核心主题,旨在帮助开发者提升程序的运行效率和稳定性。 1. **Java虚拟机概述**:Java虚拟机是Java平台的核心组件,它...
1. **字节码与类加载机制**:Java程序编译后生成的是.class文件,里面包含的是字节码。JVM通过类加载器将这些字节码加载到内存中,进行验证、准备、解析和初始化等步骤,形成运行时的数据区。 2. **运行时数据区**...