JVM学习笔记(六):类加载的时机

标签: jvm 学习 笔记 | 发表时间:2012-07-18 14:58 | 作者:
出处:http://www.iteye.com

本文根据《深入理解java虚拟机》第7章部分内容整理

 

    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类的符号引用入口了。

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

 

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [jvm 学习 笔记] 推荐:

JVM学习笔记(六):类加载的时机

- - ITeye博客
本文根据《深入理解java虚拟机》第7章部分内容整理.     Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的加载机制.    类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸载(Unloading)七个阶段.

学习JVM的References

- LightingMan - 淘宝JAVA中间件团队博客
本blog中列举了我学习JVM的references,会不断的更新,为了避免版权问题,就不在blog上提供references的下载了,感兴趣的同学可自行下载或购买,:). |— [ Hotspot GC论文 ]. |— [ 其他JVM GC ]. |— Linux内核源代码情景分析. |— Linux 内核中断内幕.

JVM原理分析笔记

- - Java - 编程语言 - ITeye博客
1.Javac编译器的作用. 将符合Java语言规范的源代码转化成符合Java虚拟机规范的Java字节码. 2.编译器主要的几个处理阶段. 词法分析、语法分析、语义分析和代码生成,基于访问者模式来遍历语法树的过程. 二.ClassLoader. 将Class加载到JVM中,审查每个类应该由谁加载,将Class字节码重新解析成JVM统一要求的对象格式.

JVM学习 - 体系结构

- - CSDN博客推荐文章
一:Java技术体系模块图. 二:JVM运行时内存区域模型. 也称"永久代” 、“非堆”,  它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域. 可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小. 运行时常量池:是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后放到方法区的运行时常量池中.

JVM学习之:浅谈方法调用

- - CSDN博客推荐文章
前面提到了几种方法的调用,虚拟机也提供了对应的字节码指令,分别是:. invokestatic:调用静态方法. invokespecial:调用构造器方法,私有方法以及父类方法. invokevirtual:调用虚方法以及final方法(虽然用invokevirtual调用,可是因为final方法的不可覆盖性,因此也是非虚方法).

JVM内存管理学习总结(一)

- - CSDN博客互联网推荐文章
I.JVM进程的生命周期. JVM实例的生命周期和java程序的生命周期保持一致,即一个新的程序启动则产生一个新的JVM进程实例,程序结束则JVM进程实例伴随着消失. 那么程序启动和程序终止就是JVM实例生命周期的两个边界,两个边界点可以这么理解:一个拥有程序入口(main函数)的class在执行main方法时,相应的JVM就被创建了(即JVM生命周期的起点),当由此main函数启动的所有非守护线程都终止时,JVM即退出(JVM实例生命周期的终点).

shell 学习笔记

- tiger - 游戏人生
将脚本目录加到 PATH 中. 在 dash 中如何进行字符串替换. 将 rst 格式文档转换为 blog 可用的 html 代码. shell 脚本虽然不是非常复杂的程序, 但对于首次接触的我来讲, 多少还是有些忌惮. 不过, 接触任何新事物都需要勇敢面对, 逐步树立信心. 我是冲着把脚本写好去的, 所以, 我的目标是能够写出友好, 健壮, 优美的脚本..

OAuth学习笔记

- 宋大妈 - FeedzShare
来自: 标点符 - FeedzShare  . 发布时间:2011年08月29日,  已有 2 人推荐. OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据.

Vim学习笔记

- 临池学书 - C++博客-首页原创精华区
最近在学习Vimtutor中的相关内容,Vim的使用博大精深,很多命令一旦不使用就会忘记,下面把其中的没有使用到的相关命令做一个简单的总结,供以后复习使用. 至于常见的保存,插入等等命令,则不予记录,在以后的使用中加深练习即可. To change until the end of a word, type  ce (ce + 修正的单词).

OAuth学习笔记

- jiaosq - 标点符
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据. 每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频).