JVM(五)——类加载阶段

一、类加载阶段

一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载
(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化
(Initialization)、使用(Using)和卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称
为连接(Linking)。
类的声明周期
加载、验证、准备、初始化和卸载阶段的加载顺序是确定的,而解析阶段不一定:它在某些情况下可以在初始化阶段之后再开始。这是为了支持Java语言的运行时绑定特性(也称为动态绑定或晚期绑定)。

1)加载

“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的一个阶段。

  • 将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field 有:
    • _java_mirror 即 java 的类镜像,例如对 String 来说,就是 String.class,作用是把 klass 暴
    • 露给 java 使用
    • _super 即父类
    • _fields 即成员变量
    • _methods 即方法
    • _constants 即常量池
    • _class_loader 即类加载器
    • _vtable 虚方法表
    • _itable 接口方法表

注意:

  • instanceKlass 这样的【元数据】是存储在方法区(1.8 后的元空间内),但 _java_mirror是存储在堆中的
  • 可以通过前面介绍的 HSDB 工具查看

在这里插入图片描述

2)连接

2.1 验证

验证类是否符合 JVM 规范,安全性检查。

2.2 准备

准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段(默认值)。这里仅包括类变量,而不包括实例变量。

  • 设置默认值static 变量在 JDK 7 之前存储于 instanceKlass 末尾,从 JDK 7 开始,存储于 _java_mirror 末尾
  • static 变量分配空间和赋值是两个步骤,分配空间在准备阶段完成,真正的赋值在初始化阶段完成
  • 如果 static 变量是 final 的基本类型,以及字符串常量,那么编译阶段值就确定了,赋值在准备阶
    段完成
  • 如果 static 变量被 final 修饰,但属于引用类型,那么赋值也会在初始化阶段完成

2.3 解析

将常量池中的符号引用解析为直接引用

package cn.itcast.jvm.t3.load;
/**
* 解析的含义
*/
public class Load2 {
	public static void main(String[] args) throws ClassNotFoundException, IOException {
		ClassLoader classloader = Load2.class.getClassLoader();
		// loadClass 方法不会导致类的解析和初始化 创建了C的对象,但不会创建 D的对象
		Class<?> c = classloader.loadClass("cn.itcast.jvm.t3.load.C");
		// 创建了 C 和 D的对象
		// new C();	
		System.in.read();
	}
}
class C {
	D d = new D();
}
class D {
}

3)初始化阶段

<clinit>()V 方法
初始化即调用 <clinit>()V ,虚拟机会保证这个类的『构造方法』的线程安全
发生的时机
类初始化是【懒惰的】

  • main 方法所在的类,总会被首先初始化
  • 首次访问这个类的静态变量或静态方法时
  • 子类初始化,如果父类还没初始化,会引发
  • 子类访问父类的静态变量,只会触发父类的初始化
  • Class.forName
  • new 会导致初始化

不会导致类初始化的情况

  • 访问类的 static final 静态常量(基本类型和字符串)不会触发初始化
  • 类对象.class 不会触发初始化
  • 创建该类的数组不会触发初始化
  • 类加载器的 loadClass 方法
  • Class.forName 的参数 2 为 false 时

二、类加载器

以 JDK 8 为例:

名称加载哪的类说明
Bootstrap ClassLoaderJAVA_HOME/jre/lib无法直接访问
Extension ClassLoaderJAVA_HOME/jre/lib/ext上级为 Bootstrap, 显示 null
Application ClassLoaderclasspath上级为 Extension
自定义类加载自定义上级为 Application

一般情况下 获取的 classloader 默认是 AppClassLoader 应用程序类加载器

1)启动类加载器

E:\git\jvm\out\production\jvm>java -Xbootclasspath/a:. cn.itcast.jvm.t3.load.Load5
bootstrap F init
null
  • -Xbootclasspath 表示设置 bootclasspath (Bootstrap ClassLoader)
  • 其中 /a:. 表示将当前目录追加至 bootclasspath 之后

2)扩展类加载器

将A类打包成 jar包,放在 JAVA_HOME/jre/ext目录下,获取A类的类加载器为 sun.misc.Launcher$ExtClassLoader$@23232
通过双亲委派机制,往上找类加载器,委派上级优先查找加载,如果上级没有,最后由本级加载(应用程序类加载器)

3)双亲委派模式

所谓的双亲委派,就是指调用类加载器的 loadClass 方法时,查找类的规则

注意
这里的双亲,翻译为上级似乎更为合适,因为它们并没有继承关系

protected Class<?> loadClass(String name, boolean resolve)
	throws ClassNotFoundException {
	synchronized (getClassLoadingLock(name)) {
		// 1. 检查该类是否已经加载
		Class<?> c = findLoadedClass(name);
		if (c == null) {
			long t0 = System.nanoTime();
			try {
				if (parent != null) {
					// 2. 有上级的话,委派上级 loadClass
					c = parent.loadClass(name, false);
				} else {
					// 3. 如果没有上级了(ExtClassLoader),则委派 BootstrapClassLoader
					c = findBootstrapClassOrNull(name);
				}
			} catch (ClassNotFoundException e) {
			}
			if (c == null) {
				long t1 = System.nanoTime();
				// 4. 每一层找不到,调用 findClass 方法(每个类加载器自己扩展)来加载
				c = findClass(name);
				// 5. 记录耗时
				sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
				sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
				sun.misc.PerfCounter.getFindClasses().increment();
			}
		}
		if (resolve) {
			resolveClass(c);
		}
		return c;
	}
}

4)线程上下文类加载器

我们在使用 JDBC 时,都需要加载 Driver 驱动,但是实际上我们不写Class.forName("com.mysql.jdbc.Driver"),也可以让com.mysql.jdbc.Driver正确加载。底层是

public class DriverManager {
	// 注册驱动的集合
	private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers 
		= new CopyOnWriteArrayList<>();
	// 初始化驱动
	static {
		loadInitialDrivers();
		println("JDBC DriverManager initialized");
	}

进入loadInitialDrivers() 方法

private static void loadInitialDrivers() {
	...
	// 1)使用 ServiceLoader 机制加载驱动,即 SPI
	AccessController.doPrivileged(new PrivilegedAction<Void>() {
		publicVoid run() {
			ServiceLoader<Driver> loadedDrivers =
			ServiceLoader.load(Driver.class);
			Iterator<Driver> driversIterator = loadedDrivers.iterator();
			try{
				while(driversIterator.hasNext()) {
					driversIterator.next();
				}
			} catch(Throwable t) {
				// Do nothing
			}
			return null;
		}
	});
	println("DriverManager.initialize: jdbc.drivers = " + drivers);
	// 2)使用 jdbc.drivers 定义的驱动名加载驱动
	if (drivers == null || drivers.equals("")) {
		return;
	}
	String[] driversList = drivers.split(":");
	println("number of Drivers:" + driversList.length);
	for (String aDriver : driversList) {
		try {
			println("DriverManager.Initialize: loading " + aDriver);
			// 这里的 ClassLoader.getSystemClassLoader() 就是应用程序类加载器
			Class.forName(aDriver, true,	
			ClassLoader.getSystemClassLoader());
		} catch (Exception ex) {
			println("DriverManager.Initialize: load failed: " + ex);
		}
	}
}

底层实际上是 spi(Service Provider Interface)机制。
最后是使用 Class.forName 完成类的加载和初始化,关联的是应用程序类加载器,因此可以顺利完成类加载

约定如下,在 jar 包的 META-INF/services 包下,以接口全限定名名为文件,文件内容是实现类名称
在这里插入图片描述
体现的是【面向接口编程+解耦】的思想,在下面一些框架中都运用了此思想:

  • JDBC
  • Servlet 初始化器
  • Spring 容器
  • Dubbo(对 SPI 进行了扩展)

5)自定义类加载器

在这里插入图片描述
在自定义类加载器时,使用不同的类加载器对象获取的类不是相同的。
例如:

public class Load7 {
    public static void main(String[] args) throws Exception {
        MyClassLoader classLoader = new MyClassLoader();
        Class<?> c1 = classLoader.loadClass("MapImpl1");
        Class<?> c2 = classLoader.loadClass("MapImpl1");	// 都是 classLoader1 对象
        System.out.println(c1 == c2);	// true

        MyClassLoader classLoader2 = new MyClassLoader();
        Class<?> c3 = classLoader2.loadClass("MapImpl1");	// 创建了 classLoader2 
        System.out.println(c1 == c3);	// false
    }
}

三、运行期优化

1)即时编译

分层编译

JVM 将执行状态分成了 5 个层次:

  • 0 层,解释执行(Interpreter)
  • 1 层,使用 C1 即时编译器编译执行(不带 profiling)
  • 2 层,使用 C1 即时编译器编译执行(带基本的 profiling)
  • 3 层,使用 C1 即时编译器编译执行(带完全的 profiling)
  • 4 层,使用 C2 即时编译器编译执行

即时编译器:c1 客户端编译器(Client Compiler) c2 服务端编译器 (Server Compiler)

profiling 是指在运行过程中收集一些程序执行状态的数据,例如【方法的调用次数】,【循环的
回边次数】等

即时编译器(JIT)与解释器的区别

  • 解释器是将字节码解释为机器码,下次即使遇到相同的字节码,仍会执行重复的解释
  • JIT 是将一些字节码编译为机器码,并存入 Code Cache,下次遇到相同的代码,直接执行,无需再编译
  • 解释器是将字节码解释为针对所有平台都通用的机器码
  • JIT 会根据平台类型,生成平台特定的机器码

对于占据大部分的不常用的代码,我们无需耗费时间将其编译成机器码,而是采取解释执行的方式运行;另一方面,对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想的运行速度。 执行效率上简单比较一下 Interpreter < C1 < C2,总的目标是发现热点代码(hotspot名称的由来),优化之

优化手段称之为【逃逸分析】,发现新建的对象是否逃逸。可以使用 -XX:-DoEscapeAnalysis 关闭逃逸分析,再运行刚才的示例观察结果,未使用 C2 即时编译执行。

方法内联

对热点方法(经常调用的方法),且代码长度较短,会进行内联,即将方法内的代码拷贝、粘贴到方法调用者的位置。

-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining (解锁隐藏参数)打印 inlining(内联) 信息
-XX:CompileCommand=dontinline,*JIT2.square 禁止某个方法 inlining
-XX:+PrintCompilation 打印编译信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/494302.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HCIA-Datacom实验_03_实验一:华为VRP系统基本操作

1.运行eNSP&#xff0c;设置-界面设置-自定义界面-设备标签&#xff0c;“总显示接口标签” 打钩。 2.按照实验拓扑添加设备 注&#xff1a;如果是真实环境&#xff0c;需要接两条线&#xff1a; &#xff08;1&#xff09;串口线&#xff1a;电脑USB口到网络设备Console口&am…

第二篇:3.1 广告印象(AD Impression) - IAB与MRC及《增强现实广告效果测量指南1.0》

--- 我为什么要翻译美国IAB科技公司系列标准 翻译计划 第一篇概述—IAB与MRC及《增强现实广告效果测量指南》之目录、适用范围及术语第二篇广告效果测量定义和其他矩阵之- 3.1 广告印象&#xff08;AD Impression&#xff09;第三篇广告效果测量定义和其他矩阵之- 3.2 可见度 …

网络安全-提权篇

我们在文件包含的时候可以将错误的用户名包含进日志&#xff0c;但是权限问题让人很烦恼&#xff0c;今天的侧重点主要是跟大家聊一聊提权 用户名包含进日志参考&#xff1a;RCE with LFI and SSH Log Poisoning - Hacking Articles 目录 一、环境 二、看看权限&#xff08;…

17、GateWay和Sentinel继承实现服务限流

注&#xff1a;本篇文章主要参考周阳老师讲解的cloud进行整理的&#xff01; 1、需求说明 cloudalibaba-sentinel-gateway9528 保护 cloudalibaba-provider-payment9001 2、启动nacos服务器8848 startup.cmd -m standalone 3、启动sentinel服务器8080 java -jar sentinel-dash…

基于java实现学科竞赛管理系统【Springboot+mybatis+layui】

基于java实现学科竞赛管理系统【Springbootmybatislayui】 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文…

深入Facebook的世界:探索数字化社交的无限可能性

引言 随着数字化时代的到来&#xff0c;社交媒体平台已经成为了人们日常生活中不可或缺的一部分&#xff0c;而其中最为突出的代表之一便是Facebook。作为全球最大的社交媒体平台之一&#xff0c;Facebook不仅仅是一个社交网络&#xff0c;更是一个数字化社交的生态系统&#…

2024年云计算使用报告,89%组织用多云,25%广泛使用生成式AI,45%需要跨云数据集成,节省成本是云首要因素

备注&#xff1a;本文来自Flexera2024年的云现状调研报告的翻译。原报告地址&#xff1a; https://info.flexera.com/CM-REPORT-State-of-the-Cloud Flexera是一家专注于做SaaS的IT解决方案公司&#xff0c;有30年发展历史&#xff0c;5万名客户&#xff0c;1300名员工。Flex…

鸿蒙OS开发实例:【工具类封装-http请求】

import http from ohos.net.http; import promptAction from ohos.promptAction; 封装HTTP接口请求类&#xff0c;提供格式化的响应信息输出功能。 使用 DevEco Studio 3.1.1 Release 及以上版本&#xff0c;API 版本为 api 9 及以上。 示例&#xff1a; import { MyHttpUtil…

代码随想录 Day59 单调栈

42接雨水问题&#xff0c;很巧妙&#xff0c;我一开始的思路是需要两个单调栈&#xff0c;一个是递增&#xff0c;一个是递减&#xff0c;分别遍历&#xff0c;从而得到当前方块的与两边的高度差&#xff0c;但是看过卡哥的思路&#xff0c;就会明白其实另一次的比较已经在入栈…

Docker中常见的命令行

1 docker的全部命令 docker attach #连接到正在运行中的容器 docker build #使用 Dockerfile 创建镜像 docker builder #管理builds docker builder prune #清除build缓存 docker checkpoint #管理checkpoints docker checkpoint create #从正在运行的容器创建检…

3d放上模型为什么渲染不出来---模大狮模型网

如果在3D软件中放置模型后无法正确渲染出来&#xff0c;可能有几个常见的原因导致这种情况发生&#xff1a; 材质设置问题&#xff1a;确保所放置的模型具有正确的材质和纹理&#xff0c;并且材质设置正确。如果材质设置有误&#xff0c;可能会导致模型无法正确显示。 光照设置…

【面试题】数据底层原理:Elasticsearch写入流程解析

前言&#xff1a;本篇博客将介绍Elasticsearch的数据底层原理&#xff0c;涉及数据写入的过程以及相关概念。我们将深入探讨buffer、translog、refresh、commit、flush和merge等核心概念&#xff0c;帮助您更好地理解Elasticsearch的数据存储机制。 写入数据的基本过程 Elast…

BaseDao封装JavaWeb的增删改查

目录 什么是BaseDao&#xff1f; 为什么需要BaseDao&#xff1f; BaseDao的实现逻辑 什么是BaseDao&#xff1f; Basedao 是一种基于数据访问对象&#xff08;Data Access Object&#xff09;模式的设计方法。它是一个用于处理数据库操作的基础类&#xff0c;负责封装数据库…

不显示excel中零值方法

excel中想让数字0不显示的方法如下&#xff1a; √去掉则数字格式0不再显示 。若找不到此项&#xff0c;运行以下代码即可&#xff1a; Sub 去除excel中零值() ActiveWindow.DisplayZeros False 不显示零值 End Sub altf11打开vba idea&#xff0c;插入->模块&#xff…

多维数组和交错数组笔记

1.) 关于数据的几个概念&#xff1a; Rank&#xff0c;即数组的维数&#xff0c;其值是数组类型的方括号之间逗号个数加上1。 Demo&#xff1a;利用一维数组显示斐波那契数列F(n) F(n-1) F(n-2) (n >2 ),每行显示5项,20项. static void Main(string[] args){int[] F n…

C++初学者:像C#一样优雅写程序

C语言的强大&#xff0c;在于自由自在&#xff0c;你可以随便怎么写程序&#xff0c;不讲格式&#xff0c;可以很整齐&#xff0c;也可以鬼画符&#xff0c;只要能运行就可以了&#xff0c;程序员可以自由发挥。 如图&#xff1a; 国际C语言大赛中的代码 图2 C的语言更强大&…

机器人深度学习IMU和图像数据实现焊接精细操作

在双电极气体保护金属弧焊 &#xff08;DE-GMAW&#xff09; 中&#xff0c;对焊枪和旁路电极位置的精确控制是至关重要的。为了这一过程&#xff0c;科研团队提出了安装微型惯性测量单元&#xff08;IMU&#xff09;传感器和摄像头&#xff0c;来记录焊工控制焊枪的移动和微调…

pyhive入门介绍和实例分析(探索票价与景点评分之间是否存在相关性)

介绍 PyHive 是一组 Python DB-API 和 SQLAlchemy 接口&#xff0c;可用于 Presto 和 Hive。它为 Python 提供了一个与 Presto 和 Hive 进行交互的平台&#xff0c;使得数据分析师和工程师可以更方便地进行数据处理和分析。 以下是使用 PyHive 进行数据分析时需要注意的几点&…

Vue时间组件:Dayjs与Moment对比

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

FFmpeg初步了解

一、了解FFmpeg 1.1 什么是FFmpeg FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec&#xff0c;为了保证…