基于源码剖析:深度解读JVM底层运行机制

news2025/1/2 16:56:31

每日禅语

佛说,给你修路的,是你自己;埋葬你的,也是你自己;帮助你的,是你自己;毁灭你的,也是你自己;成就你的,自然还是你自己。所以佛说:自作自受,自性自度!

文章背景

在我们Java Coder编写java代码的时候,通过编译器点击运行,然后就可以输出想要的结果,但是如果你仔细询问这段代码是如何通过java代码,变成.class文件,再通过JVM执行输出结果的时候,大部分人都不能很清楚的说出运行机制和运行原理。或者对于大部分面试者来说,JVM了解的都是JVM八股文信息,但是你要深究为什么需要这些区域,这些区域如果关联起来运行的,大部分人都难以说出其重点。本文就是以此为基础,通过源码一步一步分析程序的底层运行原理。 

通过一道面试题分析底层:输出的结构是多少?

public class Client {
    public static void main(String[] args) {
        int count = 0;
        for (int i = 0 ; i <10 ;i ++){
            count = count++;
            System.out.println("count=" + count);
        }
    }
}

 大部分人的第一印象答案:输出1-9,但是如果你让程序运行以后你会发现,结果和你想的大相径庭,竟然是输出的10个0,为什么结果和预期的差距这么大。后面的内容将会根据这段代码分析JVM的内存区域模型,从而让你知其然而知其所以然。

Java文件编译成class文件以后如何从磁盘读入内存中 

详细讲解

Java字节码详细解析

 javap -c .\Demo.class

public static void main(java.lang.String[]);
Code:
   0: iconst_0                 // 将常量 0 压入操作数栈,栈顶是 0
   1: istore_1                 // 将栈顶的 0 存储到局部变量 1 (即变量 count)
   2: iconst_0                 // 将常量 0 压入操作数栈,栈顶是 0
   3: istore_2                 // 将栈顶的 0 存储到局部变量 2 (即变量 i)
   4: iload_2                 // 加载局部变量 2 (即 i) 到操作数栈
   5: bipush        10         // 将常量 10 压入操作数栈
   7: if_icmpge     33         // 比较栈顶两个整数 (i和 10),如果 i >= 10,跳转到字节码地址 33 (即结束循环)
  10: iload_1                 // 加载局部变量 1 (即 count) 到操作数栈
  11: iinc          1, 1      // 将局部变量 1 (count) 增加 1
  14: istore_1                 // 将栈顶的值 (更新后的 count) 存储回局部变量 1
  15: getstatic     #7        // 获取静态字段 System.out (即输出流)
  18: iload_1                 // 加载局部变量 1 (即 count) 到操作数栈
  19: invokedynamic #13,  0   // 使用动态方法调用输出 (这里是用 makeConcatWithConstants 动态拼接字符串)
  24: invokevirtual #17       // 调用 PrintStream 的 println 方法,将拼接的字符串打印出来
  27: iinc          2, 1      // 将局部变量 2 (i) 增加 1
  30: goto          4         // 跳转回到字节码地址 4,继续循环
  33: return                  // 返回,结束程序

 代码片段2:

public class Client {
    public static void main(String[] args) {
        int count = 0;
        for (int i = 0 ; i <10 ;i ++){
            count = ++count;
            System.out.println("count=" + count);
        }
    }
}
  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iconst_0
       3: istore_2
       4: iload_2
       5: bipush        10
       7: if_icmpge     33
      10: iinc          1, 1
      13: iload_1
      14: istore_1
      15: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      18: iload_1
      19: invokedynamic #13,  0             // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
      24: invokevirtual #17                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      27: iinc          2, 1
      30: goto          4
      33: return
}

 count++的字节码操作:

  10: iload_1                 // 加载局部变量 1 (即 count) 到操作数栈
  11: iinc          1, 1      // 将局部变量 1 (count) 增加 1
  14: istore_1                 // 将栈顶的值 (更新后的 count) 存储回局部变量 1

总结:

  • 先将局部变量表位置为1的count的值为0加载到操作数栈 

  • 再将局部变量1中的count + 1

  • 再将操作数栈的值为1又更新到局部变量表1位置,虽然第二步已经将值加1,但是此时又给覆盖了,所以值始终为0

++count的字节码操作:

iinc 1, 1      // 将局部变量1的值加1
iload_1        // 将局部变量1的值加载到操作数栈
istore_1       // 将操作数栈顶的值存储到局部变量1

总结:

  • 将局部变量表位置为1的count的值加1

  • 再将局部变量表位置为1的count的值加载到操作数栈 

  • 再将操作数栈的值更新到局部变量表1位置,此时值就变为了1

操作数栈的作用:操作数栈的主要作用是提供一个地方来暂时存储数据,供执行各种指令时使用。它支持多种操作,如加载数据、执行算术运算、比较数据等。

局部变量表:用于存储方法的局部变量及一些相关信息。它在方法执行期间提供对局部变量的访问,并与操作数栈一起支持字节码的执行

局部变量表的数据结构

public class LocalVarExample {
    public LocalVarExample() {
    }

    public void testMethod(int a, long b) {
        int c = a + 1;
        float d = (float)c * 2.5F;
    }
}
局部变量表的数据结构
1.线性数组
  • 数组结构
    局部变量表以数组的形式实现,数组的每个元素称为一个 Slot(槽位)
    • 每个槽位固定为 32 位(4 字节)。
    • 一个槽位可以存储:
      • 一个 32 位的基本数据类型(如 intfloat 等)。
      • 一个引用类型(如对象引用)。
      • 一部分特殊数据(如 returnAddress,用于 jsr 和 ret 指令)。
    • 64 位数据类型(long 和 double)会占用两个连续的槽位
2. 索引定位
  • 每个局部变量通过索引(Index)定位。
    • 索引从 0 开始。
    • 方法的第一个参数存储在索引为 0 的槽位,后续参数依次存储。
    • 方法参数结束后,局部变量存储在后续槽位。
3. 数据类型和槽位

局部变量表中没有显式记录变量的类型,但类型信息隐含在字节码指令中:

  • 例如,iload_1 表示从索引 1 的槽位加载一个 int 类型变量。
 局部变量表的内存分配
  1. 初始化

    • 局部变量表的大小在方法调用时确定,大小由方法的 max_locals 指定。
    • max_locals 的值由编译器在编译时计算,表示方法所需的局部变量槽位的总数。
  2. 参数分配

    • 静态方法:
      • 方法参数按顺序分配到槽位。
    • 实例方法:
      • 索引 0 存储 this 引用,其他参数按顺序分配。
  3. 局部变量分配

    • 方法体中声明的局部变量在调用时分配到空闲的槽位。

计算局部变量表的大小

局部变量表的大小取决于:

  1. 方法的参数
  2. 方法体内声明的局部变量
  3. 变量类型的大小

规则

  • 每个局部变量占用一个或多个 Slot
    • 32 位变量(intfloatreferencereturnAddress):占用 1 个 Slot。
    • 64 位变量(longdouble):占用 2 个连续的 Slot。
  • 静态方法不包含 this 引用。
  • 非静态方法的第一个 Slot 用于存储 this 引用。
局部变量表的操作
  1.  加载和存储

    局部变量表与操作数栈之间通过加载(load)和存储(store)指令交互:

    • 加载指令(如 iloadfloadaload):
      • 从局部变量表加载数据到操作数栈。
    • 存储指令(如 istorefstoreastore):
      • 从操作数栈存储数据到局部变量表。
    2. 类型相关性
    • 加载和存储指令类型敏感。例如:
      • iload_1 表示从槽位 1 加载 int 类型。
      • dload_2 表示从槽位 2 和 3 加载 double 类型。

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

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

相关文章

算法进阶:贪心算法

贪心算法是一种简单而直观的算法思想&#xff0c;它在每一步选择中都采取在当前状态下最优的选择&#xff0c;以期望最终得到全局最优解。贪心算法通常适用于一些具有最优子结构的问题&#xff0c;即问题的最优解可以通过一系列局部最优解的选择得到。 贪心算法的基本思路是&a…

Hive刷分区MSCK

一、MSCK刷分区 我们平时通常是通过alter table add partition方式增加Hive的分区的&#xff0c;但有时候会通过HDFS put/cp命令或flink、flum程序往表目录下拷贝分区目录&#xff0c;如果目录多&#xff0c;需要执行多条alter语句&#xff0c;非常麻烦。Hive提供了一个"…

Windows API Set:那些“只存在但不被使用“的DLL

API Set 是什么&#xff1f; 想象一下&#xff0c;Windows就像一个大型图书馆&#xff0c;而API Set就是这个图书馆的索引系统。但这个索引系统非常特别&#xff1a;它是直接内置在Windows加载器中的"虚拟目录"。 // 一个典型的API Set映射示例 api-ms-win-core-mem…

【Java 数据结构】合并两个有序链表

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 目录 1. 题目 2. 解析 3. 代码实现 4. 小结 1. 题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示…

图像处理-Ch6-彩色图像处理

Ch6 彩色图像处理 无广告更易阅读&#xff0c;个人博客点此进入<– 文章目录 Ch6 彩色图像处理彩色基础彩色模型(Color models)RGB(red, green, blue)CMY & CMYK(cyan, magenta, yellow/and black)HSI(hue, saturation, intensity)HSV(hue, saturation, value) 颜色空…

03.HTTPS的实现原理-HTTPS的工作流程

03.HTTPS的实现原理-HTTPS的工作流程 简介1. HTTPS的工作流程1.1. TCP的工作流程1.1.1. 三次握手的详细步骤1.1.2. 三次握手的作用 1.2. HTTPS的工作流程1.2.1. HTTPS与TCP的关系1.2.2. HTTPS的工作流程 2. 公钥和私钥的作用3. 对称密钥的生成和交换4. 对称加密和非对称加密的区…

隧道FM广播信号、隧道内调频广播信号覆盖方案选择

一、为什么汽车驶入隧道内&#xff0c;就听不到FM调频广播信号了 隧道是一个半封闭的管状结构&#xff0c;有很强的电磁屏蔽效应&#xff0c;汽车进入隧道后&#xff0c;汽车收音机就会出现沙沙的噪声&#xff0c;这是由于隧道内的调频广播信号变弱甚至无信号&#xff0c;导致车…

基于SSM的“电器网上订购系统”的设计与实现(源码+数据库+文档+PPT)

基于SSM的“电器网上订购系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页 商品类型 商品管理 订单展示 商品购物车 登录页面 …

工业大数据分析算法实战-day19

day19 今天是第19天&#xff0c;昨日是针对线性规划、整数规划建模技巧进行阐述&#xff0c;今天开启第九章节—行业知识沉淀的方法&#xff0c;该章节主要是对行业知识的范畴进行探讨&#xff0c;将讨论限制在研判类的知识沉淀上&#xff0c;将业务范围侧重在PHM中&#xff0…

Unity URP多光源支持,多光源阴影投射,多光源阴影接收(优化版)

目录 前言&#xff1a; 一、属性 二、SubShader 三、ForwardLitPass 定义Tags 声明变体 声明变量 定义结构体 顶点Shader 片元Shader 四、全代码 四、添加官方的LitShader代码 五、全代码 六、效果图 七、结语 前言&#xff1a; 哈喽啊&#xff0c;我又来啦。这…

8086汇编(16位汇编)学习笔记09.宏汇编

8086汇编(16位汇编)学习笔记09.宏汇编-C/C基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net 宏汇编在文件中是当做关键字的,但是在bug中运行时并没有这些指令,这些关键词被称为伪指令,cpu并不认识他们,需要经过编译器转化成 cpu认识的代码,但是他多我们写代码帮助又…

leetcode 面试经典 150 题:矩阵置零

链接矩阵置零题序号73题型二维数组解题方法标记数组法难度中等熟练度✅✅✅✅ 题目 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1]…

五、CentOS7/CentOS8安装APISIX(1)

目录 &#x1f33b;&#x1f33b; 一、 Apache APISIX介绍1.1 什么是Apache APISIX1.2 APISIX架构1.3 Apache APISIX 的技术优势1.4 APISIX‌应用场景 二、APISIX快速开始2.1 centos7/centos8安装APISIX 一、 Apache APISIX介绍 1.1 什么是Apache APISIX Apache APISIX 是一个…

BUG分析 - 重启有时失败

1. 倒查版本 1.0_11 - ok1.0_12 - fail 2.对比1.0_11和1.0_12 失败时的日志 ================================== 1.0_11 ============================== 2024-12-26 09:46:51.886 INFO [26332] [ThreadPLCPool::in

Ngnix介绍、安装、实战及用法!!!

一、Nginx简介 1、Nginx概述 Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理服务器&#xff0c;特点是占有内存少&#xff0c;并发能力强&#xff0c;能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数 。 2、正向代理 正向代理&#xff1a;如果把局…

【物联网】给EoRa Pi 烧录Meshtastic

文章目录 一、Meshtastic 是什么&#xff1f;二、Meshtastic 烧录过程1. 在线烧录工具2. 刷机进度 总结 一、Meshtastic 是什么&#xff1f; Meshtastic 是一种基于 LoRa 技术的离网通信平台。它通过低成本、低功耗的无线电设备&#xff0c;实现远距离自组网通信。可在脱离现有…

Java 中的各种锁

​ Java 中我们经常听到各种锁&#xff0c;例如悲观锁&#xff0c;乐观锁&#xff0c;自旋锁等等。今天我们将 Java 中的所有锁放到一起比较一下&#xff0c;并分析各自锁的特点&#xff0c;让大家能够快捷的理解相关知识。 1、悲观锁 VS 乐观锁 从概念上来说 悲观锁: ​ 在…

【开源免费】基于SpringBoot+Vue.JS租房管理系统(JAVA毕业设计)

本文项目编号 T 102 &#xff0c;文末自助获取源码 \color{red}{T102&#xff0c;文末自助获取源码} T102&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

报考重庆大学计算机研究生有哪些要求?

想要报考重庆大学计算机研究生&#xff0c;首要你要确定考专硕还是学硕&#xff0c;考什么内容&#xff0c;需要买什么书&#xff1f;以及考研期间一些信息获取渠道等。下面C哥将对以上问题进行详细解答。 1.报考条件&#xff1a; 报考重大计算机研究生与重大其他大多数专业条…

爬虫与反爬虫实现全流程

我选取的网页爬取的是ppt nba版 需要的工具:pycharm,浏览器 爬虫需要观察它的网页信息,然后开始首先爬取它的html,可以看到有人气,标题,日期,咨询 可以看到用get方法 import requests url"https://img-home.csdnimg.cn/images/20230724024159.png?origin_urlhttps%3A%2…