聊聊Java虚拟机(一)—— 类加载子系统

news2024/12/22 22:54:48

1. 前言

​ 虚拟机就是一款用来执行虚拟计算机指令的计算机软件。它相当于一台虚拟计算机。大体上,虚拟机分为系统虚拟机和程序虚拟机。系统虚拟机就相当于一台物理电脑,里面可以安装操作系统;程序虚拟机是为了执行单个计算机程序而设计出来的虚拟机。其中 Java 虚拟机就是执行 Java 字节码指令的虚拟机

JVM 是什么?

java 虚拟机是运行在各大平台的执行字节码文件的虚拟计算机。如下图所示

这样的设计可以让编译后的代码在各个操作平台上运行。

JVM 的位置

JVM 在 Java 体系结构中的位置

从用户操作角度看 JVM 所处的位置

JVM 与实际的计算机硬件没有交互,它们中间还有个操作系统,调用硬件需要通过操作系统来实现。

JVM 的结构

JVM 的整体运行结构

本文主要针对于 Hotspot VM 来进行,其结构如下所示:

JVM 的指令结构

JVM 是基于栈的指令集架构,跟基于寄存器的指令集不同。它是一个步骤一个一条指令。

//基于栈的指令集
iconst_2 //常量2入栈
istore_1
iconst_3 // 常量3入栈
istore_2
iload_1
iload_2
iadd //常量2/3出栈,执行相加
istore_0 // 结果5入栈
//基于寄存器的指令集
mov eax,2 //在eax寄存器中的值设为2
add eax,3 //将eax寄存器中的值加3   

举例说明:

/**
 * @author wjw
 * @date 11/3/2021
 */
public class StackStruTest {
    public static void main(String[] args) {
        int i = 2 + 3;
        /**
         *0: iconst_5
         *1: istore_1
         *2: return
         */
        int i = 2;
        int j = 3;
        int k = i + j;
        /**
         * 0: iconst_2
         * 1: istore_1
         * 2: iconst_3
         * 3: istore_2
         * 4: iload_1
         * 5: iload_2
         * 6: iadd
         * 7: istore_3
         * 8: return
         */
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("hello");
    }
}

JVM 的生命周期

JVM 的启动

Java 的启动是通过引导类加载器(BootStrap ClassLoader)创建一个初始类(java.lang.class)来实现的,也就是后面要提到的子类加载器过程。

JVM 的执行

执行 Java 的字节码文件中的各个方法和类的过程。

JVM 的退出

  • 程序正常执行结束
  • 操作系统终止 JVM 进程终止
  • 某线程调用 Runtime.exit() 或 System.halt() 方法
  • 程序执行过程异常或错误而终止退出

2. 类加载子系统的主要流程

让我们先看看一个 java 程序执行的流程是怎样的:

  • 首先一个 java 文件编写完后,经过 java 编译器生成 .class 文件
  • 这个 .class 文件经过类加载器,加载各类的 jar 包,以及该程序所需要的三方类的 .class 文件。并生成 java.lang.class 对象,这个对象将作为程序访问方法区中的这些类型数据的外部接口
  • . class 文件经过解析执行和 JIT 编译器编译执行,并调用操作系统相关指令来完成 java 程序。

而类加载子系统涉及到的过程有类加载器、字节码校验器和翻译字节码这几个过程:

加载阶段(Loading)

加载阶段(Loading)是类加载阶段(Class Loading)的一部分。在加载阶段中,.class 文件将经过三个类加载器的加载后才能进入下一个阶段。该阶段主要的作用(也就是上面 java 程序执行中的类加载器阶段)有:

  • 将各种各样格式的 .class 文件(jar 包,网络中,动态代理等等)读取并生成一个字节流,并转换成方法区中的运行时数据结构
  • 在内存中生成一个 java.lang.Class 对象,这个对象将作为程序访问方法区中的这些类型数据的外部接口

在类加载器中,主要分为两类:引导类加载器自定义类加载器

引导类加载器(Bootstrap ClassLoader)
  • 使用的是 C/C++ 来实现的,无法访问到,调用getClassLoader() 函数是为null。

  • 加载的类是一些核心类库,lib/home 下的类库。比如说是 Object 、String 等等 JVM 自身运行需要的类

  • 是其他加载器的父类吗?是的,但是无父类加载器

自定义类加载器(User-Defined ClassLoader)

派生于抽象类 ClassLoader 的类加载器

  • (1)拓展类加载器(Extension ClassLoader)

    • 父类加载器为 BootStrap ClassLoader

    • 主要加载的类是ext 文件夹下的类库

  • (2)应用程序类加载器(AppClassLoader)

    • 父类加载器为 Extension ClassLoader

    • 主要加载的是环境变量 classpath 或系统属性 java.class.path 指定路径下的类库

    • 程序中默认的类加载器

  • (3) 自定义类加载器

    • extCassLoader 和 AppClassLoader 是 sun.misc.Launcher 的两个内部类

为了防止虚拟机内部的核心类库的修改和非法调用,JVM 的设计者们在类加载过程中设计出了双亲委派机制沙箱安全机制

双亲委派机制
  • 当前类加载器先不加载,先委托其父类加载器,依次到启动加载器
  • 如果启动加载器还没找到所要的类,然后向下查找,到返回到最初的子类加载器
  • 类加载器之间的关系不是继承,而是通过组合来复用父加载器的代码。

举例

  • 首先在 Bootstrap ClassLoader 中加载核心 rt.jar 包中的核心类
  • 核心类中的一些三方接口在反向委派到应用程序加载器加载第三方的 jar 包
  • 是通过当前线程加载器(就是系统类加载器)加载到 jdbc.jar 包中的 SPI 接口实现类

双亲委派机制的优点:

  • 避免类的重复加载
  • 保护程序安全,防止核心 API 被随意篡改
沙箱安全机制

引导类加载器在加载时会首先加载 JDK 中自带的文件。报错信息中没有相应方法时的机制。保证对 java 核心源代码的保护。

链接阶段(Linking)
验证(Verify)

验证字节流文件中的相关信息,确认当前文件符合虚拟机的相关格式(文件、元数据、符号等等)。

准备(Prepare)

在方法区中为类变量分配内存并设置类变量初始值

  • 这里的类变量是指被 static 修饰的变量,不包括实例变量(实例变量在对象实例化时随着对象一起分配)
  • 初始值指的是数据类型的零值
解析(Resolve)
  • 虚拟机将常量池中的符号引用转换成直接引用(class 文件很小,先存放指向所需要类的符号,解析时再引用)

  • 针对类、接口、字段方法等7类符号引用进行转换

  • 没有规定解析阶段发生的具体时间,也可能在初始化阶段的后面

初始化阶段(Initiation)
  • 初始化过程实际上就是执行类构造器方法 ( ) 的过程

    • 它是 javac 编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来,不需要定义。
    public class ClassInitTest {
        private static int num = 1;
    
        static {
            num = 2;
            number = 20;
            System.out.println(num);//因为在linking 阶段中的 Prepare 阶段已经给静态变量赋过初始值了。
            //非法的前向引用;在声明变量之前,可以去给这些变量进行赋值,但是不能够调用它。
            //System.out.println(number);//会报错误
        }
    
        /**也就是在 linking 中的 prepare 中对变量进行默认赋值为0
         * 此时initial 给予其一个覆盖从 20 到 10
         *
         */
        private static int number = 10;
    
        public static void main(StringDemo[] args)
        {
            System.out.println(ClassInitTest.num);
            System.out.println(ClassInitTest.number);
        }
    }
    
    • 它不需要显示调用父类构造器,在子类的( ) 执行前,父类的 ( ) 方法早已执行完毕。

  • 构造器方法执行是按语句顺序来执行的。

  • 类构造器方法 ( ) 和类构造器( ) 并不是一回事

    • ( ) 方法不需要定义,而( ) 需要定义(不定义但是会有个默认为空的构造器方法)
    • 只有当代码中包含 static 变量时,才会执行 ( ) 方法,而( ) 所有类的字节码中都有


  • 在多线程时,初始化只执行一次,否则会被上锁

3.其他小结

  • 确定两个类是否相同
    • 包名、类名完全相同
    • 所用的类加载器也要相同
  • 类的主动使用和被动使用
    • 也就是是否有初始化过程,是否调用 clinit<>() 方法

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FPGA之分布式RAM(2)

1) 128 X1 Single Port Distributed RAM 下图中可以看出来,通过2个LUT的组合使用可以串联实现更大深度的分布式RAM.下图中出现了F7BMUX的加入, F7BMUX可以用于LUT输出的选通. 原语调用&#xff1a; RAM128XIS#(INIT(128h00000000000000000000000000000000) // Initial conten…

动态规划——炮兵回城【集训笔记】

题目描述 游戏盘面是一个m行n列的方格矩阵&#xff0c;将每个方格用坐标表示&#xff0c;行坐标从下到上依次递增&#xff0c;列坐标从左至右依次递增&#xff0c;左下角方格的坐标为(1,1)&#xff0c;则右上角方格的坐标为(m,n)。 游戏结束盘上只剩下一枚炮兵没有回到城池中&a…

ERP系统哪个好用?用友,金蝶,ORACLE,SAP综合测评

ERP系统哪个好用&#xff1f;用友&#xff0c;金蝶&#xff0c;ORACLE&#xff0c;SAP综合测评 ERP领域SAP、ORACLE相对于国内厂商如用友、金蝶优势在哪&#xff1f; SAP&#xff0c;ORACLE操作习惯一般国人用不惯&#xff1b;相对于国产软件&#xff0c;界面也很难看&#x…

深入理解C语言(2):字符、字符串与内存函数

文章主题&#xff1a;字符、字符串与内存函数&#x1f30f;所属专栏&#xff1a;深入理解C语言&#x1f4d4;作者简介&#xff1a;更新有关深入理解C语言知识的博主一枚&#xff0c;记录分享自己对C语言的深入解读。&#x1f606;个人主页&#xff1a;[₽]的个人主页&#x1f3…

buctoj——2024寒假集训 进阶训练赛 (五)

问题 A: 约瑟夫问题 题目描述 N个人围成一圈&#xff0c;从第一个人开始报数&#xff0c;数到M的人出圈&#xff1b;再由下一个人开始报数&#xff0c;数到M的人出圈&#xff1b;……输出依次出圈的人的编号。N&#xff0c;M由键盘输入。 输入 一行&#xff0c;包含两个正整数…

C++中命名空间、缺省参数、函数重载

目录 1.命名空间 2.缺省参数 3.函数重载 1.命名空间 在C中定义命名空间我们需要用到namespace关键字&#xff0c;后面跟上命名空间的名字&#xff0c;结构框架有点类似结构体&#xff08;如图所示&#xff09; 上面的代码我一一进行讲解&#xff1a; 1.我们先来说第三行和main函…

shopee智利选品:如何在Shopee智利站点上进行有效的选品

在Shopee智利站点进行选品时&#xff0c;卖家需要采取一系列策略来提高产品的市场接受度和销售潜力。以下是一些建议&#xff0c;可以帮助卖家在Shopee智利站点上进行有效的选品。 先给大家推荐一款shopee知虾数据运营工具知虾免费体验地址&#xff08;复制浏览器打开&#xf…

多场景建模:阿里多场景多任务元学习方法M2M

multi-scenario multi-task meta learning approach (M2M) 背景 广告领域大部分是针对用户建模的&#xff0c;像点击率预估&#xff0c;很少有针对广告主需求建模&#xff08;广告消耗预估、活跃率/流失率预估、广告曝光量预估&#xff09;&#xff0c;广告的类型较多&#x…

GPU与SSD间的P2P DMA访问机制

基于PCIe&#xff08;Peripheral Component Interconnect Express&#xff09;总线连接CPU、独立GPU和NVMe SSD的系统架构。 在该架构中&#xff0c;PCIe Swicth支持GPU与SSD之间快速的点对点直接内存访问&#xff08;peer-to-peer, p2p DMA&#xff09;。通常情况下&#xff0…

Verilog基础:强度建模与net型信号的多驱动问题(三)

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 四、一般情况下的net型信号的线与组合&#xff08;线网多驱动&#xff09; 在Verilog基础&#xff1a;强度建模与net型信号的多驱动问题&#xff08;二&#xff0…

【Redis】更改redis中的value值

今天继续进步一点点~~ 背景&#xff1a;今天有个前端的同事问我&#xff0c;能不能在Redis中他本人登录公众号的 sessionID 加上一列openID 于是我上网查了一堆在Redis里面的命令&#xff0c;以及不同的客户端怎么输入命令&#xff0c;但是后来问了下同事&#xff0c;他就给我…

压缩字符串

#include <iostream> using namespace std; int main() {// 请在此输入您的代码string a;int f 1, count;cin >> a;for (int i 0; a[i]; i) {if (a[i] a[i 1]) {f 0; count 1;for (i; a[i] a[i 1]; i)count 1;cout << a[i] << count;}if (cou…

vivado JTAG链、连接、IP关联规则

JTAG链 这列出了定义板上可用的不同JTAG链。每个链都列在下面<jtag_chain>以及链的名称&#xff0c;以及定义名称和链中组件的位置&#xff1a; <jtag_chains> <jtag_chain name"chain1"> <position name"0" component"part0…

RK3568笔记十:Zlmediakit交叉编译

若该文为原创文章&#xff0c;转载请注明原文出处。 编译Zlmediakit的主要目的是想实现在RK3568拉取多路RTPS流&#xff0c;并通过MPP硬解码&#xff0c;DRM显示出来。为了实现拉取多路流选择了Zlmediakit,使用FFMEPG也可以&#xff0c;在RV1126上已经验证了可行性。 一、环境…

《Linux高性能服务器编程》笔记05

Linux高性能服务器编程 本文是读书笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考 Linux高性能服务器编程源码: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服务器编程 文章目录 Linux高性能服务器编程第12章 高性能I/O框架库Libevent12.1 I/…

线程池--JAVA

虽然线程是轻量级进程&#xff0c;但是如果当创建和销毁的的频率非常之高&#xff0c;那么它也就会消耗很多的资源。 而线程池就是用来优化线程频繁创建和销毁的场景&#xff0c;减少线程创建、销毁的频率。 ExecutorService JAVA标准库为我们实现了线程池&#xff0c;Execu…

(二十四)Kubernetes系列之Helm3

Helm为kubernetes的包管理工具&#xff0c;就像Linux下的包管理器&#xff08;yum/apt等&#xff09;&#xff0c;可以很方便的将之前打包好的yaml文件部署到kubernetes上。 1.安装访问地址&#xff1a;https://github.com/helm/helm/releases 点击查看最新的版本&#xff0c…

【C/C++】C/C++编程——为什么学习 C++?

当提到C的时候&#xff0c;很多人会觉得语法复杂、学习曲线陡峭&#xff0c;并且好像与C语言还有点"纠缠不清"。尽管如此&#xff0c;C仍然是当今世界上最受欢迎和最有影响力的编程语言之一。特别是在当今快速发展的人工智能&#xff08;AI&#xff09;领域&#xff…

性能利器Caffeine缓存全面指南

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;今天咱们聊聊Caffeine缓存&#xff0c;小黑在网上购物&#xff0c;每次查看商品都要等几秒钟&#xff0c;那体验肯定不咋地。但如果用了缓存&#xff0c;常见的商品信息就像放在口袋里一样&#xff0c;随时取用&…

python:socket基础操作(1)-《环境准备介绍》

我这里的环境是在真机上下载了一个 pycharm 进行编写 并且还开了两台虚拟机 一台为win7 下载了一个网络调试助手&#xff0c;因为等会编写的时候分为客户端和服务端&#xff0c;当我们编写了客户端或者服务端&#xff0c;可以用图形化的网络调试助手去当对象&#xff0c;图形化…