深入理解JVM 第七章 虚拟机类加载机制

Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的加载机制。

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸载(Unloading)七个阶段。其中验证、准备和解析三个部分统称为连接(Linking),这七个阶段的发生顺序如下图所示:

如上图所示,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类的加载过程必须按照这个顺序来按部就班地开始,而解析阶段则不一定,它在某些情况下可以在初始化阶段后再开始。

类的生命周期的每一个阶段通常都是互相交叉混合式进行的,通常会在一个阶段执行的过程中调用或激活另外一个阶段。

Java虚拟机规范没有强制性约束在什么时候开始类加载过程,但是对于初始化阶段,虚拟机规范则严格规定了有且只有四种情况必需立即对类进行“初始化”(而加载、验证、准备阶段则必需在此之前开始),这四种情况归类如下:

1.遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令最常见的Java代码场景是:使用new关键字实例化对象时、读取或者设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)时、以及调用一个类的静态方法的时候。

2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

3.当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要触发父类的初始化。

4.当虚拟机启动时,用户需要指定一个执行的主类(包含main()方法的类),虚拟机会先初始化这个类。

对于这四种触发类进行初始化的场景,在java虚拟机规范中限定了“有且只有”这四种场景会触发。这四种场景的行为称为对类的主动引用,除此以外的所有引用类的方式都不会触发类的初始化,称为被动引用。

下面通过三个实例来说明被动引用:

示例一

父类SuperClass.java

package com.chenzhou.classloading;
/**
 * ClassName:SuperClass <br/>
 * Function: 被动使用类:通过子类引用父类的静态字段,不会导致子类初始化. <br/>
 * Date:     2012-7-18 上午09:37:06 <br/>
 * @author   chenzhou
 * @version
 * @since    JDK 1.6
 * @see
 */
public class SuperClass {
    static{
        System.out.println("SuperClass init!");
    }
    public static int value = 123;
}

子类SubClass.java

package com.chenzhou.classloading;

public class SubClass extends SuperClass {
    static{
        System.out.println("SubClass init!");
    }
}

主类NotInitialization.java

package com.chenzhou.classloading;
/**
 * ClassName:NotInitialization <br/>
 * Function: 非主动使用类字段演示. <br/>
 * Date:     2012-7-18 上午09:41:14 <br/>
 * @author   chenzhou
 * @version
 * @since    JDK 1.6
 * @see
 */
public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(SubClass.value);
    }
}

输出结果:

SuperClass init!
123

由结果可以看出只输出了“SuperClass init!”,没有输出“SubClass init!”。这是因为对于静态字段,只有直接定义该字段的类才会被初始化,因此当我们通过子类来引用父类中定义的静态字段时,只会触发父类的初始化,而不会触发子类的初始化。

示例二

父类SuperClass.java如上一个示例一样

主类NotInitialization.java

package com.chenzhou.classloading;
/**
 * ClassName:NotInitialization <br/>
 * Function: 通过数组定义来引用类,不会触发此类的初始化. <br/>
 * Date:     2012-7-18 上午09:41:14 <br/>
 * @author   chenzhou
 * @version
 * @since    JDK 1.6
 * @see
 */
public class NotInitialization {
    public static void main(String[] args) {
        SuperClass[] scs = new SuperClass[10];
    }
}

输出结果为空

没有输出“SuperClass init!”说明没有触发类com.chenzhou.classloading.SuperClass的初始化阶段,但是这段代码会触发“[Lcom.chenzhou.classloading.SuperClass”类的初始化阶段。这个类是由虚拟机自动生成的,该创建动作由newarray触发。

示例三

常量类ConstClass.java

package com.chenzhou.classloading;
/**
 * ClassName:ConstClass <br/>
 * Function: 常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化. <br/>
 * Reason:     TODO ADD REASON. <br/>
 * Date:     2012-7-18 上午09:46:56 <br/>
 * @author   chenzhou
 * @version
 * @since    JDK 1.6
 * @see
 */
public class ConstClass {
    static{
        System.out.println("ConstClass init!");
    }

    public static final String HELLOWORLD = "hello world";
}

主类NotInitialization.java

package com.chenzhou.classloading;
/**
 * ClassName:NotInitialization <br/>
 * Function: 非主动实用类字段演示. <br/>
 * Date:     2012-7-18 上午09:41:14 <br/>
 * @author   chenzhou
 * @version
 * @since    JDK 1.6
 * @see
 */
public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(ConstClass.HELLOWORLD);
    }
}

输出:hello world

上面的示例代码运行后也没有输出“SuperClass init!”,这是因为虽然在Java源码中引用了ConstClass类中的常量HELLOWORLD,但是在编译阶段将此常量的值“hello world”存储到了NotInitialization类的常量池中,对于常量ConstClass.HELLOWORLD的引用实际上都被转化为NotInitialization类对自身常量池的引用了。实际上NotInitialization的Class文件之中已经不存在ConstClass类的符号引用入口了。

接口的加载过程与类加载的区别在于上面提到的四种场景中的第三种,当类在初始化时要求其父类都已经初始化过了,但是一个接口在初始化时,并不要求其父类都完成了初始化,只有在真正用到父类接口的时候(如引用父接口的常量)才会初始化。

时间: 2024-07-31 12:57:30

深入理解JVM 第七章 虚拟机类加载机制的相关文章

第七章 虚拟机类加载机制

JVM把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成JVM可以直接使用的Java类型的过程就是类加载机制. 1. 类加载的时机 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Using).卸载(Unloading)七个阶段,其中验证.准备.解析三个部分统称连接.顺序如下:

第七章 虚拟机类加载机制 总结

代码编译的结果从本地 机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步. 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. (一)类加载的时机 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载,验证,准备,解析,初始化,使用和卸载7个阶段.其中验证,准备,解析3个部分统称为连接 (二)类加载的过程 详细说一下Java虚拟机中类加载的全过程,也就是

第七章——虚拟机类加载机制——学习总结

类的生命周期 7.2.开始类加载.类初始化的时机. 1)类加载过程的第一个阶段:加载.虚拟机规范中没有进行强制约束,交由具体的虚拟机实现来自由把握. 2)初始化 阶段,虚拟机规范中严格规定了有且只有4种情况必须立即对类进行"初始化"(加载,验证,准备自然在初始化之前开始). a)遇到new.getstatic.putstatic.invokestatic四条字节码指令时,若类没被初始化,则要立即初始化. b)使用java.lang.reflect包的方法对类进行反射调用时,若类没被初始

《深入理解Java虚拟机》-----第7章 虚拟机类加载机制——Java高级开发必须懂的

代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步. 7.1 概述 上一章我们了解了Class文件存储格式的具体细节,在Class文件中描述的各种信息,最终都需要加载到虚拟机中之后才能运行和使用.而虚拟机如何加载这些Class文件?Class文件中的信息进入到虚拟机后会发生什么变化?这些都是本章将要讲解的内容. 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载

虚拟机类加载机制——类加载时机

由于道行不够深,所以此篇类加载机制的讲解主要来自于<深入理解Java虚拟机——JVM高级特性与最佳实践>的第7章 虚拟机类加载机制. 在前面<初识Java反射>中我们在开头提到要了解Java反射,就得要了解虚拟机的类加载机制.在这里,我们来试着窥探一下何为类加载. “虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,类型的加载.连接和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制.”这句话确实读着好懂,但到底类加载做了什么呢?我们都知道

深入理解JVM读书笔记三: 虚拟机类加载机制

Java虚拟机类加载机制是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 7.1概述 与那些在编译时需要进行链接工作的语言不同,在Java语言里面,类型的加载和链接过程都是在程序运行期间完成的(其实C++也是分为静态链接库和动态链接库的),这样会在类加载时稍微增加一些性能开销,但是却能为Java应用程序提供高度的灵活性,Java中天生可以动态扩展的语言特性就是依赖运行期动态加载和动态链接这个特点实现的. 7.

【JVM】虚拟机类加载机制

什么是类加载 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. <[JVM]类文件结构>讲的是Class文件结构,即我们编写的Java代码(.java文件)经过编译后生成Class文件(.class文件).这一章讲述的是如何将这个Class文件加载到内存并最终形成虚拟机直接使用Java类型的过程. 1.类加载的时机 类的生命周期 类的生命周期 其中,加载.验证.准备.初始化和卸载这5个顺序

深入理解Java虚拟机-----------虚拟机类加载机制

虚拟机类加载机制 类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期包括:加载,验证,准备,解析,初始化,使用,卸载等7个阶段.其中,验证,准备,解析3个部分称为连接. 以上7个阶段中,加载,验证,准备,初始化和卸载五个阶段的顺序是确定的,类的加载过程必需按照这种顺序按部就班的开始(开始并不意味着按部就班的"进行"或"完成",因为这些阶段通常是互相交叉地混合式进行的).而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运

Java面试准备之JVM详细研究三(类加载机制)

类加载过程 一个类从编写完成后,编译为字节码之后,它要装载进内存有七个阶段: 加载 => (验证-> 准备-> 解析)=> 初始化=> 使用=> 卸载 括号中的三个步骤可以整合成为 “连接”步骤.其中的步骤并不是一个阶段结束,一个阶段才开始的.只是说他们的开始阶段基本遵循此顺序(解析阶段更是可能在使用的时候才发生,目的是配合动态绑定),这些阶段都是互相交叉的混合式进行的,通常会在一个阶段执行过程中调用或激活另一个阶段. 1.加载 ”加载“的过程是”类加载“过程的一个阶段