Java虚拟机类加载过程

小编 2026-06-16 阅读:1206 评论:0
文章摘自:深入理解Java虚拟机 第二版 周志明著  本文主要讲解一个Java虚拟机中类加载的全过程,也就是加载、验证、准备、解析和初始化这5个阶段所执行的具体动作。 加载 首先,加载是类加载C...

文章摘自:深入理解Java虚拟机 第二版 周志明著 

本文主要讲解一个Java虚拟机中类加载的全过程,也就是加载、验证、准备、解析和初始化这5个阶段所执行的具体动作。

  • 加载

首先,加载是类加载Class Loading过程的一个阶段

然后,在加载阶段,虚拟机需要完成以下3件事:

1)通过一个类的全限定名来获取定义此类的二进制字节流。

2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。

可以看出虚拟机 规范 的这3点要求并不算具体,如第一条并没有指明二进制字节流要从一个Class文件中获取,这样的好处可想而知,可以让充满创造力的开发人员在这个规范上玩各种各种的花样,许多Java技术都建立在这个基础上,如:从ZIP包获取,最终成为日后JAR、WAR格式的基础;运行时计算生成,这种使用最多的就是动态代理技术,在java.lang.reflect.Proxy中,就是用了ProxyGenerator。generateProxyClass来为特定接口生成形式为‘$Proxy’代理类的二进制字节流;由其他文件生成,典型是JSP,即由JSP文件生成对应的Class类;由数据库中读取等等……

加载阶段既可以使用系统提供的引导类加载器完成,也可以由用户自定义的类加载器去完成,开发人员可以通过定义自己的类加载器去控制字节流的获取方式(即重写一个类加载器的loadCalss()方法)

加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中,方法区中的数据存储格式由虚拟机实现自行定义,虚拟机规范未规定此区域的具体数据结构。然后在内存中实例化一个java.lang.Class类的对象,这个对象将作为程序访问方法区中的这些类型数据的外部接口。

加载阶段与连接阶段部分内容交叉进行,即加载阶段未完成,连接阶段可能已经开始。

  • 验证

验证是连接阶段第一步,目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。因为Class文件可以使用任何途径产生,所以安全性需要保证,验证则是虚拟机对自身保护的一项重要工作。

对于虚拟机的类加载机制来说,验证阶段是一个非常重要但是不一定必要的阶段,如果所运行的代码都已经被反复使用和验证过,那么在实施阶段就可以考虑使用-Xverify:None参数关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

  • 准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这个阶段中n进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。而这里所说的初始值通常是数据类型的零值。  假设一个类变量public static int value = 123;那么变量value在准备阶段过后初始值为0,不是123。而把value赋值为123是程序被编译后,存放于类构造器<clinit>()方法之中,所以把value赋值为123的动作将在初始化阶段执行。

基本数据类型的零值:

boolean        false

char              \'/uoooo\'(null)

byte              (byte)0

short             (short)0

int                  0

long               0L

float               0.0f

double           0.0d

reference       null

上述提到通常为零值,其实还有一些特殊清空:如果类字段的字段属性表存在ConstantValue属性,那在准备阶段变量value就会被初始化为ConstantValue所指定的值,  假设一个类变量public static final int value = 123;那么准备阶段虚拟机会根据ConstantValue的设置将vlaue赋值为123.

  • 解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程

  • 初始化

类初始化阶段真正开始执行类中定义的Java程序代码。

准备阶段变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序猿通过程序指定的主观计划去初始化类变量和其他资源,或者可以这样说:初始化阶段是执行类构造器<client>()方法的过程。

<client>()方法 与类的构造方法(或者说实例构造器<init>()方法)不同,它不需要显示地调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。因此 在虚拟机中第一个被执行了<clinit>方法的类肯定是java.lang.Object

由于父类的<client>()方法先执行,也就意味着父类中定义的静态语句块要有优先于子类的变量赋值操作。

package com.gary.test.clinit;

public class Test {
	static class Parent {
		public static int A = 1;
		static {
			A = 2;
		}
	}
	
	static class Sub extends Parent {
		public static int B = A;
	}

	public static void main(String[] args) {
		System.out.println(Sub.B);
	}

}
2

<client>()方法对于类和接口来说并不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成<client>()方法。

接口中不能使用静态语句块,但是仍然有变量初始化的赋值操作,因此接口与类一样都会生成<client>()方法。但接口与类不同的是,执行接口的<client>()方法不需要先执行父接口的<client>()方法。只有当父接口中定义的变量使用时,父接口才回初始化。另外,接口的实现类在初始化时也一样不会执行接口的<client>()方法。

虚拟机会保证一个类的<client>()方法在多线程环境中被正确的枷锁、同步,如果多个线程同时去初始化一个类,那么只有一个线程会去执行这个类的<client>()方法;其他线程都需要阻塞等待,直到活动线程执行<client>()方法完毕。

package com.gary.test.clinit;

public class Test2 {
	static class DeepLoopClass {
		static {
			if (true) {
				System.out.println(Thread.currentThread() + \"init DeepLoopClass\");
				while (true) {
				}
			}
		}
	}

	public static void main(String[] args) {
		Runnable runnable = new Runnable() {
			public void run() {
				System.out.println(Thread.currentThread() + \"start\");
				DeepLoopClass dlc = new DeepLoopClass();
				System.out.println(Thread.currentThread() + \"run over\");
			}
		};
		Thread thread1 = new Thread(runnable);
		Thread thread2 = new Thread(runnable);
		thread1.start();
		thread2.start();
	}

}

\"\"

可以看出另外一条线程一直在阻塞。 

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表