【JVM】字节码指令

news2024/9/22 11:30:20

文章目录

  • 1. 方法的执行流程
    • 1.1 常量池载入运行时常量池
    • 1.2 方法字节码载入方法区
    • 1.3 main线程开始运行,分配栈帧内存
    • 1.4 执行引擎开始执行字节码
  • 2. 条件判断
    • 2.1 源码分析
  • 3. 循环控制指令
    • 3.1 源码分析

1. 方法的执行流程

原始Java代码

public class Demo3_1 {
    public static void main(String[] args) {
        int a = 10;
        int b = Short.MAX_VALUE + 1;
        int c = a + b;
        System.out.println(c);
    }
}

编译后的字节码文件

[root@localhost ~]# javap -v Demo3_1.class
Classfile /root/Demo3_1.class
Last modified Jul 7, 2019; size 665 bytes
MD5 checksum a2c29a22421e218d4924d31e6990cfc5
Compiled from "Demo3_1.java"
public class cn.itcast.jvm.t3.bytecode.Demo3_1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#26 // java/lang/Object."<init>":()V
#2 = Class #27 // java/lang/Short
#3 = Integer 32768
#4 = Fieldref #28.#29 //
java/lang/System.out:Ljava/io/PrintStream;
#5 = Methodref #30.#31 // java/io/PrintStream.println:(I)V
#6 = Class #32 // cn/itcast/jvm/t3/bytecode/Demo3_1
#7 = Class #33 // java/lang/Object
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Lcn/itcast/jvm/t3/bytecode/Demo3_1;
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 args
#18 = Utf8 [Ljava/lang/String;
#19 = Utf8 a
#20 = Utf8 I
#21 = Utf8 b
#22 = Utf8 c
#23 = Utf8 MethodParameters
#24 = Utf8 SourceFile
#25 = Utf8 Demo3_1.java
#26 = NameAndType #8:#9 // "<init>":()V
#27 = Utf8 java/lang/Short
#28 = Class #34 // java/lang/System
#29 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
#30 = Class #37 // java/io/PrintStream
#31 = NameAndType #38:#39 // println:(I)V
#32 = Utf8 cn/itcast/jvm/t3/bytecode/Demo3_1
#33 = Utf8 java/lang/Object
#34 = Utf8 java/lang/System
#35 = Utf8 out
#36 = Utf8 Ljava/io/PrintStream;
#37 = Utf8 java/io/PrintStream
#38 = Utf8 println
#39 = Utf8 (I)V
{
public cn.itcast.jvm.t3.bytecode.Demo3_1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."
<init>":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/itcast/jvm/t3/bytecode/Demo3_1;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: bipush 10
2: istore_1
3: ldc #3 // int 32768
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: getstatic #4 // Field
java/lang/System.out:Ljava/io/PrintStream;
13: iload_3
14: invokevirtual #5 // Method
java/io/PrintStream.println:(I)V
17: return
LineNumberTable:
line 8: 0
line 9: 3
3)常量池载入运行时常量池
4)方法字节码载入方法区
5)main 线程开始运行,分配栈帧内存
(stack=2,locals=4)
line 10: 6
line 11: 10
line 12: 17
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 args [Ljava/lang/String;
3 15 1 a I
6 12 2 b I
10 8 3 c I
MethodParameters:
Name Flags
args
}

1.1 常量池载入运行时常量池

首先,会由JVM的类加载器把main方法所在的这个类进行一个类加载的操作。

也就是字节码文件读取到内存中来。其中常量池这部分的数据,会放在内存的运行时常量池中。

不知道什么是运行时常量池的同学可以看看这个【JavaSE】浅析String与StringTable_起名方面没有灵感的博客-CSDN博客

image-20230306223426646

一些比较小的数字,比如10这样的数字,并不存储在运行时常量池的,它是跟着方法的字节码指令存储在一起的。

一旦这个数字超过了整数的最大值(Short.MAX_VALUE),那么就会存储在常量池中。


1.2 方法字节码载入方法区

方法的一些字节码,则会放在方法区

image-20230306224057252


1.3 main线程开始运行,分配栈帧内存

在栈帧中有两个东西,分别是局部变量表和操作数栈。

在字节码文件中已经规定了这两个东西的大小了。

Code:
stack=2, locals=4, args_size=1

也就是操作数栈为2,而局部变量表为4。

image-20230306224233281


1.4 执行引擎开始执行字节码

根据方法区的字节码文件,执行流程如下。

执行bipush 10,这个指令的意思是将一个byte压进操作数栈(其长度会补齐4字节),类似的指令还有

  • sipush将一个short压进操作数栈(其长度会补齐4个字节)
  • ldc将一个int压入操作数栈
  • ldc_w将一个long压入操作数栈(分两次压进,因为long是8字节)
  • 而小的数字都是和字节码指令存在一起的,超过short范围的数字存储在常量池

image-20230307104059705

接着,执行istore_1,这个之林的意思就是存入局部变量表slot 1

image-20230307104317435

ldc #3,这个指令是从常量池#3数据加载到操作数栈中

image-20230307104435272

istore2,接着将操作数栈中的32768放置在局部变量表的2号槽位。

image-20230307104554000

iload1 与 iload2指令是将局部变量表的1号与2号位置的数据依次放置进操作数中

image-20230307104711304

image-20230307104719638

isadd,这个指令对应的就是加法运算,执行引擎会去执行add,操作数栈会弹出两个变量,执行引擎完成add操作后,会再次压进操作数栈

image-20230307105020947

istore 3接着将操作数栈中的变量弹出,放置在3号位置中,也就是赋值给c

image-20230307105107244

getstatic #4,到常量池中查找一个静态对象,加载进操作数中,接着load 3将3号槽位的数据压进操作数,invokevirtual #5指令就是找到常量池#5项,定位到方法去java/io/PrintStream.println(I)方法,生成新的栈帧,传递参数并执行新栈帧的字节码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B9AX35Ax-1678159317918)(https://lnnu-tuchuang.oss-cn-guangzhou.aliyuncs.com/img/image-20230307105849420.png)]

执行完毕后,弹出栈帧,并且清除main操作数栈的内容。

image-20230307105927217

最后return,完成main方法的调用,弹出main栈帧,程序结束。


2. 条件判断

条件判断的指令如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XCr9T3UI-1678159317919)(https://lnnu-tuchuang.oss-cn-guangzhou.aliyuncs.com/img/image-20230307110332183.png)]

需要注意的是,byte,short,char 都会按 int 比较,因为操作数栈都是 4 字节


2.1 源码分析

public class Demo3_3 {
    public static void main(String[] args) {
        int a = 0;
        if(a == 0) {
            a = 10;
        } else {
            a = 20;
        }
    }
}
0: iconst_0
1: istore_1
2: iload_1
3: ifne 12
6: bipush 10
8: istore_1
9: goto 15
12: bipush 20
14: istore_1
15: return
  1. iconst_0,得到一个0的常量,压进操作数栈中
  2. istore_1,将这个操作数栈的这个元素放到局部变量表的1号位置,也就是给a赋值为0
  3. iload_1,再将a的值压进操作数栈
  4. ifne 12,判断是否 == 0
    1. 如果 != 0,那么就跳到12行字节码指令bipush 20中区,也就是向操作数栈中压入20,istore_1,将a赋值为20.
    2. 如果 == 0,那就是继续执行bipush 10istore_1,将a赋值为10,并且执行goto 15的时候,跳到第15行return

3. 循环控制指令

循环控制指令其实和条件判断差不多,只是利用了goto进行了循环


3.1 源码分析

public class Demo3_4 {
    public static void main(String[] args) {
        int a = 0;
        while (a < 10) {
            a++;
        }
    }
}
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 14
8: iinc 1, 1
11: goto 2
14: return
  1. iconst_0istore_1,将a赋值为0。
  2. iload_1将a的值压进操作数栈
  3. bipush 10,将10压进操作数栈
  4. if_icmpge 14,判断两个int是否>=,如果不成立,则跳到14行指令去
  5. 如果成立,那么iinc 1, 1,局部变量表的1号位置,自增1,接着goto 2,继续跳回2号位置。

参考:黑马程序员JVM完整教程,Java虚拟机快速入门,全程干货不拖沓)

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

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

相关文章

vue el-switch 列表开关状态显示有误 全部关闭的问题

后台使用int类型传状态status的值 但是前端列表展示的开关状态是未开启&#xff0c;实际上&#xff0c;后台传的都是开启的状态 结果应该是这样 确定后台传的status值 在 el-switch 标签中是否使用了正确的值判断&#xff0c;比如 后台用的是字符串、布尔 或者是 数值类型&…

2-7 SpringCloud快速开发入门: Eureka 注册中心高可用集群搭建

接上一章节Eureka 服务注册中心发现与消费服务&#xff0c;这里讲讲Eureka 注册中心高可用集群搭建 Eureka 注册中心高可用集群搭建 Eureka 注册中心高可用集群就是各个注册中心相互注册 Eureka Server的高可用实际上就是将自己作为服务向其他服务注册中心注册自己&#xff0c…

LeetCode——203. 移除链表元素

对于初学链表的学者来学&#xff0c;链表是比较困难的&#xff0c;这部分对指针结构体的要求比较高。我们通过练习是掌握知识的重要途经203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09;我们在数组中去除某元素是遍历一遍数组&#xff0c;如果某位置是要去除的元素&a…

[Linux]应用部署部分流程命令备忘

备忘一下常用的Linxu应用部署命令&#xff0c;Java应用版。 目录1、环境查询1.1、端口占用查询1.2、环境变量查询与设置设置局部用户定义变量设置全局环境变量删除环境变量2、执行命令保存日志并查看3、查看java应用内存使用情况1、环境查询 1.1、端口占用查询 lsof -i 命令 …

leetcode-每日一题-2379(简单,字符串)

久违的简单题......给你一个长度为 n 下标从 0 开始的字符串 blocks &#xff0c;blocks[i] 要么是 W 要么是 B &#xff0c;表示第 i 块的颜色。字符 W 和 B 分别表示白色和黑色。给你一个整数 k &#xff0c;表示想要 连续 黑色块的数目。每一次操作中&#xff0c;你可以选择…

NGINX学习笔记(三):一篇搞懂NGINX的常用配置之LOCATION指令

写在前面 NGINX主配置文件 /etc/nginx/nginx.conf 是一个纯文本类型的文件&#xff0c;整个配置文件是以区块的形式组织&#xff0c;通常每一个区块以一对大括号{}来表示开始与结束。 提示&#xff1a;若编译安装则nginx.conf 位于编译时所指定目录。 我是手动编译安装的&…

【LeetCode】剑指 Offer(20)

目录 题目&#xff1a;剑指 Offer 38. 字符串的排列 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 写在最后&#xff1a; 题目&#xff1a;剑指 Offer 38. 字符串的…

一文读懂无线信道传播的各种特性

通过无线信道传播的信号沿着大量不同的路径到达目的地&#xff0c;这些不同路径称为多径。图 1 是一位沿公路驾车的典型移动用户的图形。该图描述了从发射机到接收机的众多信号路径中的三条。这些路径源自环境中物体对辐射能的散射、反射和衍射或者媒介中的折射。各种传播机制对…

mac系统手册(帮助/说明)

文章目录1. mac自带的帮助文档2. Mac使用技巧&#xff08;提示&#xff09;2.1 聚焦搜索2.2 截图&#xff08;录制屏幕&#xff09;2.3 调出右键菜单2.4 快速查看2.5 翻译2.5.1 词典解释2.5.2 翻译&#xff08;字、词和句&#xff09;3. macOS使用手册3.1 在聚焦中进行计算和转…

C51---串口发送字符串

1.Code: #include "reg52.h" #include "intrins.h" sfr AUXR 0x8E; void UartInit(void) //9600bps11.0592MHz { PCON & 0x7F; //波特率不倍速 SCON 0x50; //8位数据,可变波特率 AUXR & 0xBF; //定时器…

100种思维模型之认知资源思维模型-030

我们常说&#xff0c;一个人永远也赚不到自己认知以外的钱&#xff0c;这话的确很有道理&#xff0c;被无数人所推崇。 由此&#xff0c;不难看出&#xff0c;认知在我们的生活起着多么关键的作用。 你的认知层次越高&#xff0c;范围越广&#xff0c;就意味着你这个人所处的阶…

SpringMVC程序开发

目录 SpringMVC 1、MVC定义 2、MVC和SpringMVC之间的关系 学SpringMVC 1、Spring MVC的创建和连接 浏览器获取前端接口和后端程序连接功能实现 2、获取参数 2.1、传递单个参数/多个参数 2.2、传递对象 2.3、传递表单参数 2.4、后端参数重命名 2.5、RequestBody接收J…

postgres源码解析52 磁盘管理器--1

简介 postgres中的磁盘管理器SMGR对外提供了管理磁盘介质的接口&#xff0c;其主要实现在md.c文件中。磁盘管理器并非对磁盘上的文件直接进行操作&#xff0c;而是通过VFD机制进行文件操作。凡是对存储在磁盘中的表进行访问操作均会与磁盘管理器打交道&#xff0c;由它进行统一…

145页企业数字化转型大数据湖项目建设和运营综合解决方案WORD

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用。部分资料内容&#xff1a; 2 需求分析 2.1功能需求 数据湖的应用、管控、展示为一体&#xff0c;提供标准的服务和数据接口和报表展现方式。数据湖数据采用高效&#xff0c;可靠的存储架构。企业业务数据制订…

【0基础学爬虫】爬虫基础之网页基本结构

大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到难全方位覆盖&#xff0c;特设【0基础学…

不用vdom的lit框架学习1:安装和编译

上一篇文章讲了我们不得不在部分页面将vuepress换用其他框架的原因&#xff0c;这里我们用了一个新的&#xff0c;号称轻量级的lit框架。 主要原因&#xff1a; 1&#xff09;我们只是部分页面使用&#xff0c;不要要太重的 2&#xff09;vite默认创建有这个选项…… 我们依…

centos 7下JDK8安装

下载安装包https://www.oracle.com/java/technologies/downloads/#java8-linux上传路径 /usr/local&#xff08;替换为自己需要安装的路径&#xff09;解压tar -zxvf jdk-8u131-linux-x64.tar.gz配置环境变量[rootlocalhost java]# vi /etc/profile添加如下配置在配置文件最后&…

C++实现红黑树(RBTree) + 模拟实现map set

目录 一、红黑树(RBTree) 1.1 红黑树概念与性质 1.2 红黑树节点的定义 1.3 红黑树模拟实现 1.3.1 红黑树成员框架 1.3.2 红黑树调整情形 1.3.3 insert() 插入结点 1.3.4 IsBalanceTree() 判断是否为平衡搜索树 二、关联式容器与键值对 2.1 关联式容器概念 2.2 键值对…

python趣味编程-盒子追逐者游戏

在上一期我们用Python实现了一个奥赛罗游戏的游戏&#xff0c;这一期我们继续使用Python实现一个简单的盒子追逐追逐者游戏&#xff0c;让我们开始今天的旅程吧~ 在Python自由源代码中使用Turtle的盒子追逐者游戏 在Python中使用Turtle的盒子追逐者游戏 是一个以 python 程序设…

数据库索引原理

数据库索引的作用是做数据的快速检索&#xff0c;而快速检索实现的本质是数据结构。像二叉树、红黑树、AVL树、B树、B树、哈希等数据结构都可以实现索引&#xff0c;但其中B树效率最高。MySQL数据库索引使用的是B树。二叉树&#xff1a;二叉树中&#xff0c;左子树比根节点小&a…