【JVM】- 类加载与字节码结构1

类文件结构

ClassFile {u4             magic;u2             minor_version;u2             major_version;u2             constant_pool_count;cp_info        constant_pool[constant_pool_count-1];u2             access_flags;u2             this_class;u2             super_class;u2             interfaces_count;u2             interfaces[interfaces_count];u2             fields_count;field_info     fields[fields_count];u2             methods_count;method_info    methods[methods_count];u2             attributes_count;attribute_info attributes[attributes_count];
}

1. 魔数(magic)和版本号

  • magic (4字节): 固定值0xCAFEBABE,用于识别类文件格式
  • minor_version (2字节): 次版本号
  • major_version (2字节): 主版本号,对应Java版本
    • Java 8: 52 (0x34)
    • Java 11: 55 (0x37)
    • Java 17: 61 (0x3D)

2. 常量池(constant_pool)

  • constant_pool_count (2字节): 常量池中项的数量加1(从1开始索引)
  • constant_pool[]: 常量池表,包含多种类型的常量:
    • Class信息 (CONSTANT_Class_info)
    • 字段和方法引用 (CONSTANT_Fieldref_info, CONSTANT_Methodref_info)
    • 字符串常量 (CONSTANT_String_info)
    • 数值常量 (CONSTANT_Integer_info, CONSTANT_Float_info等)
    • 名称和描述符 (CONSTANT_NameAndType_info)
    • 方法句柄和类型 (CONSTANT_MethodHandle_info, CONSTANT_MethodType_info)
    • 动态调用点 (CONSTANT_InvokeDynamic_info)

3. 类访问标志(access_flags)

表示类或接口的访问权限和属性,如:

  • ACC_PUBLIC (0x0001): public类
  • ACC_FINAL (0x0010): final类
  • ACC_SUPER (0x0020): 使用新的invokespecial语义
  • ACC_INTERFACE (0x0200): 接口
  • ACC_ABSTRACT (0x0400): 抽象类
  • ACC_SYNTHETIC (0x1000): 编译器生成的类
  • ACC_ANNOTATION (0x2000): 注解类型
  • ACC_ENUM (0x4000): 枚举类型

4. 类和父类信息

  • this_class (2字节): 指向常量池中该类名的索引
  • super_class (2字节): 指向常量池中父类名的索引(接口的super_class是Object)

5. 接口(interfaces)

  • interfaces_count (2字节): 实现的接口数量
  • interfaces[]: 每个元素是指向常量池中接口名的索引

6. 字段(fields)

  • fields_count (2字节): 字段数量
  • field_info[]: 字段表,每个字段包括:
    • 访问标志(如public, private, static等)
    • 名称索引(指向常量池)
    • 描述符索引(指向常量池)
    • 属性表(如ConstantValue, Synthetic等)

7. 方法(methods)

  • methods_count (2字节): 方法数量
  • method_info[]: 方法表,每个方法包括:
    • 访问标志(如public, synchronized等)
    • 名称索引(指向常量池)
    • 描述符索引(指向常量池)
    • 属性表(最重要的Code属性包含字节码)

8. 属性(attributes)

  • attributes_count (2字节): 属性数量
  • attribute_info[]: 属性表,可能包含:
    • SourceFile: 源文件名
    • InnerClasses: 内部类列表
    • EnclosingMethod: 用于局部类或匿名类
    • Synthetic: 表示由编译器生成
    • Signature: 泛型签名信息
    • RuntimeVisibleAnnotations: 运行时可见注解
    • BootstrapMethods: 用于invokedynamic指令

字节码执行流程

有一段java代码如下:

public class Demo02 {public static void main(String[] args) {int a = 10;int b = a++ + ++a + a--;}
}
  1. 常量池载入运行时常量池
  2. 方法字节码载入方法区
  3. main线程开始运行,分配栈帧内存
    在这里插入图片描述

1. 变量初始化 a = 10

0: bipush 10      // 将常量10压入操作数栈
2: istore_1       // 将栈顶值(10)存储到局部变量1(a)

此时内存状态:

  • 局部变量表:a = 10
  • 操作数栈:[空]

2. 计算 a++ (第一个操作数)

3: iload_1        // 加载局部变量1(a)的值到栈顶 → 栈:[10]
4: iinc 1, 1      // 局部变量1(a)自增1 (a=11),注意这不会影响栈顶值

此时内存状态:

  • 局部变量表:a = 11
  • 操作数栈:[10] (a++表达式的值是自增前的值)

3. 计算 ++a (第二个操作数)

7: iinc 1, 1      // 局部变量1(a)先自增1 (a=12)
10: iload_1       // 然后加载a的值到栈顶 → 栈:[10, 12]

此时内存状态:

  • 局部变量表:a = 12
  • 操作数栈:[10, 12] (++a表达式的值是自增后的值)

4. 第一次加法 a++ + ++a

11: iadd          // 弹出栈顶两个值相加,结果压栈 → 10+12=22 → 栈:[22]

5. 计算 a-- (第三个操作数)

12: iload_1       // 加载a的值到栈顶 → 栈:[22, 12]
13: iinc 1, -1    // 局部变量1(a)自减1 (a=11),不影响栈顶值

此时内存状态:

  • 局部变量表:a = 11
  • 操作数栈:[22, 12] (a–表达式的值是自减前的值)

6. 第二次加法 (前两个之和) + a--

16: iadd          // 22+12=34 → 栈:[34]

7. 存储结果到b

17: istore_2      // 将栈顶值(34)存储到局部变量2(b)

最终内存状态:

  • 局部变量表:a = 11, b = 34
  • 操作数栈:[空]

完整字节码序列

0: bipush 10       // a = 10
2: istore_1
3: iload_1         // 开始计算a++
4: iinc 1, 1
7: iinc 1, 1       // 开始计算++a
10: iload_1
11: iadd           // 前两个相加
12: iload_1        // 开始计算a--
13: iinc 1, -1
16: iadd           // 与第三个相加
17: istore_2       // b = 结果

后置自增/减(i++):

  • 先使用变量的当前值参与运算
  • 执行自增/减操作
  • 字节码表现为先iload后iinc

前置自增/减(++i):

  • 先执行自增/减操作
  • 使用新值参与运算
  • 字节码表现为先iinc后iload

案例分析

分析i++

public class Demo02 {public static void main(String[] args) {int i = 0, x = 0;while(i < 10) {x = x++;++i;}System.out.println(x); // 0}
}

由于是x++,所以x先iload进入操作数栈【0】,再执行iinc进行自增【1】。自增后进行复制,又将操作数栈中的x赋值给x【0】,此时操作数栈中x的值为0。一次循环后,x的值还是0;最终x输出0。

构造方法<cinit>()V

public class Demo03 {static int i = 10;static {i = 20;}static {i = 30;}public static void main(String[] args) {System.out.println(i); // 30}
}

编译器会按照从上至下的顺序, 收集所有的static静态代码块和静态成员赋值的代码,合并成一个特殊的方法<cinit>()V

静态变量和静态代码块按代码中的书写顺序依次执行,后执行的会覆盖前边的赋值。

构造方法<init>()V

public class Demo04 {private String a = "s1";{b = 20;}private int b = 10;{a = "s2";}public Demo04(String a, int b) {this.a = a;this.b = b;}public static void main(String[] args) {Demo04 d = new Demo04("s3", 30);System.out.println(d.a + " " + d.b); // s3 30}
}

编译器会按照从上往下的顺序,收集所有的{}代码块和成员变量赋值的代码,形成新的构造方法,但是原始构造方法内的代码总是在最后边。

方法调用

public class Demo05 {private void test1(){}private final void test2(){}public void test3(){}public static void test4(){}public static void main(String[] args) {Demo05 d = new Demo05();d.test1();d.test2();d.test3();d.test4();Demo05.test4();}
}

在这里插入图片描述

每次调用方法的时候都是先把对象入栈,调用方法后再出栈。
对于使用对象调用静态方法时(紫色框),先入栈再出栈,再调用,这样相当于多了两个无效的操作。所以如果要调用静态方法时,推荐使用类调用。

多态的原理

public class Demo06 {public static void test(Animal animal) {animal.eat();System.out.println(animal);}public static void main(String[] args) throws IOException {test(new Cat());test(new Dog());System.in.read();}
}abstract class Animal {public abstract void eat();@Overridepublic String toString() {return "我是" + this.getClass().getSimpleName();}
}
class Dog extends Animal {public void eat() {System.out.println("啃骨头");}
}class Cat extends Animal {public void eat() {System.out.println("吃鱼");}
}

运行时的内存状态:

  1. test(new Cat())调用时:
    • 堆中创建Cat对象
    • 方法区中Cat类的虚方法表(vtable)包含:
      • eat() -> Cat.eat()
      • toString() -> Animal.toString()
  2. 方法调用过程:
    • JVM通过对象头中的类指针找到Cat
    • 通过虚方法表找到实际要调用的eat()实现
    • toString()调用则直接使用Animal中的实现

finally案例1

public class Demo07 {public static void main(String[] args) {int i = 0;try {i = 10;}catch (Exception e) {i = 20;}finally {i = 30;}}
}

finally中的代码会被复制三份,分别放入:try分支、catch能被匹配到的分支、catch不能被匹配到的分支,确保他一定被执行。
JVM使用异常表(Exception table)来确定异常处理跳转位置,每个条目定义了受保护的代码范围(from-to)、处理代码位置(target)和异常类型
在这里插入图片描述

finally案例2

public class Demo07 {public static int test() {try{int i = 1/0;return 10;}finally {return 20;}}public static void main(String[] args) {System.out.println(test()); // 20}
}

字节码如下:

public static int test();Code:0: iconst_1          // 将1压入栈1: iconst_0          // 将0压入栈2: idiv              // 执行除法(1/0),这里会抛出ArithmeticException3: istore_0          // (不会执行)存储结果到局部变量04: bipush 10         // (不会执行)将10压入栈6: istore_1          // (不会执行)存储到局部变量1(临时返回值)7: bipush 20         // finally块开始:将20压入栈9: ireturn           // 直接从finally块返回20// 异常处理部分10: astore_2          // 异常对象存储到局部变量211: bipush 20         // finally块:将20压入栈13: ireturn           // 从finally块返回20Exception table:from    to  target type0     7    10   any

finally块中的return会完全覆盖try块中的return或抛出的异常,这题输出20而不会抛异常。(原本的ArithmeticException被丢弃,因为finally中有return)
控制流变化:

  • 正常情况下:try → finally(return)
  • 异常情况下:try → catch → finally(return)
  • 两种路径最终都执行finally中的return

fianlly 案例3

public class Demo08 {public static int test() {int i = 10;try{return i;}finally {i = 20;}}public static void main(String[] args) {System.out.println(test()); // 10}
}

如果在try中return值了,就算在finally中修改了这个值,返回的结果也仍然不会改变,因为在return之前会先做一个暂存(固定返回值),然后执行finally中的代码,再把暂存的值恢复到栈顶, 返回的还是之前暂存的值。在这里插入图片描述

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

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

相关文章

Android及Harmonyos实现图片进度显示效果

鸿蒙Harmonyos实现&#xff0c;使用ImageKnife自定义transform来实现图片进度效果 import { Context } from ohos.abilityAccessCtrl; import { image } from kit.ImageKit; import { drawing } from kit.ArkGraphics2D; import { GrayScaleTransformation, PixelMapTransform…

linux 中的自动化之systemd

linux | 自动化之systemd linux 中的自动化之systemd 学习总是循序渐进的。 linux 中程序的自动化&#xff0c;包括早期手动启动&#xff0c;查看启动后的日志、分析启动情况&#xff0c;再到后面封装脚本(大致要求启动后检查是否挂了&#xff0c;挂了拉起&#xff0c;没挂跳过…

【编译工具】CodeRider 2.0:驭码 CodeRider 2.0 全流程智能研发协作平台深度技术测评报告

目录 前言&#xff1a;CodeRider 2.0 简介 &#xff08;1&#xff09;核心功能 &#xff08;2&#xff09;适用场景 &#xff08;3&#xff09;优势 一、产品概述与技术架构 &#xff08;1&#xff09;产品定位与核心价值 &#xff08;2&#xff09;技术架构解析 &…

抓包 TCP 四次挥手报文

文章目录 抓包 TCP 三次握手报文一、第一次挥手二、第二次挥手三、第三次挥手四、第四次挥手 抓包 TCP 三次握手报文 抓包 TCP 三次握手报文 一、第一次挥手 二、第二次挥手 三、第三次挥手 四、第四次挥手

KMP(Kotlin Multiplatform)发布Web版本乱码

一、背景 最近用KMP尝试运行在Android、iOS、desktop都成功了&#xff0c;网络数据访问也正常。 可是当运行wasmJs的时候遇到了2个较大的问题。 中文字体出现乱码。 出现了跨域问题。 首先贴一下每个平台的运行截图&#xff1a; Android iOS Desktop 二、问题 当web跑起…

一个应用程序或移动网站项目提供最佳UI解决方案

一个应用程序或移动网站项目提供最佳UI解决方案 此套件是用大量的爱和辛勤工作制作的&#xff0c;为您的下一个应用程序或移动网站项目提供最佳解决方案。120个完全可编辑的界面&#xff0c;分为10个类别和2种文件格式&#xff0c;Photoshop和AI。简单易用的结构将允许您以所…

Android studio打包生成jar包文件

Android studio打包生成jar包文件 一 项目配置1.修改 app/build.gradle2.修改 AndroidManifest.xml 二 打 Jar 包1.修改 app/build.gradle2.编译生成 Jar 包 一 项目配置 1.修改 app/build.gradle 将com.android.application改成com.android.library注释掉applicationId 2.…

JAVA类加载机制(jdk8)

三句话总结JDK8的类加载机制&#xff1a; 类缓存&#xff1a;每个类加载器对他加载过的类都有一个缓存。双亲委派&#xff1a;向上委托查找&#xff0c;向下委托加载。沙箱保护机制&#xff1a;不允许应用程序加载JDK内部的系统类。 JDK8的类加载体系 类加载器的核心方法 //…

更进一步深入的研究ObRegisterCallBack

引入 我们如果想hook对象的回调,在上篇文章里我们已经知道了对象回调函数存在一个列表里面&#xff0c;我们通过dt可以看见&#xff0c;这里他是一个LIST_ENTRY结构&#xff0c;但是实际调用的时候&#xff0c;这样是行不通的&#xff0c;说明它结构不对 0: kd> dt _OBJEC…

Nginx-3 Nginx 的负载均衡策略

Nginx-3 Nginx 的负载均衡策略 Nginx 的负载均衡其实就是指将请求按照一定的策略转发给服务集群中的一台&#xff0c;提高了服务集群的可用性&#xff0c;解决数据流量过大、网络负荷过重的问题。 AKF 扩展立方体 分为 3 个方向负载&#xff1a; x 轴&#xff1a;增加实例数…

Wiiu平台RetroArch全能模拟器美化整合包v1.18

这款WiiU平台RetroArch全能模拟器美化整合包v1.18的亮点包括&#xff1a; 1. 18款平台完美兼容&#xff1a;无论你是喜欢NES时代的经典游戏&#xff0c;还是钟爱SNES、GBA等平台的大作&#xff0c;这款整合包都能满足你的需求&#xff0c;让你尽情畅玩游戏。 2. 三款自制主题&a…

MyBatis原理

Mybatis执行过程为&#xff1a;接口代理->sqlSession会话->executor执行器->JDBC操作 一、接口代理 Mybatis根据Mapper接口&#xff0c;动态生成相应实现类 二、SqlSession介绍 MyBatis核心对象SqlSession介绍 - MyBatis中文官网 三、Executor执行器介绍 精通My…

升级内核4.19-脚本

#bash cd /root yum remove -y kernel-tools-3.10.0-1160.el7.x86_64 yum remove -y kernel-tools-libs-3.10.0-1160.el7.x86_64tar -xvf rhel-7-amd64-rpms.tar.gz cd /root/rhel-7-amd64-rpms #安装依赖、包括socat&conntrack yum localinstall -y *.rpm --skip-broken#升…

全面理解 JVM 垃圾回收(GC)机制:原理、流程与实践

JVM 的 GC&#xff08;Garbage Collection&#xff09;机制是 Java 程序性能的关键支柱。本文将从堆内存布局、回收原理、GC 算法、流程细节、并发收集器机制等维度&#xff0c;系统讲清楚 GC 的底层运作原理和优化思路。 一、JVM 堆内存结构 Java 堆是 GC 管理的主要区域&am…

runas命令让其他用户以管理员权限运行程序

RUNAS 用法: RUNAS使用示例&#xff1a; runas /noprofile /user:mymachine\administrator cmd #本机Administrator管理员身份执行CMD&#xff0c;/noprofile为不加载该用户的配置信息。runas /profile /env /user:mydomain\admin “mmc %windir%\system32\dsa.msc” #本机上…

实战指南:部署MinerU多模态文档解析API与Dify深度集成(实现解析PDF/JPG/PNG)

MinerU web api部署 MinerU 能够将包含图片、公式、表格等元素的多模态 PDF、PPT、DOCX 等文档转化为易于分析的 Markdown 格式。 克隆 MinerU 的仓库 git clone https://github.com/opendatalab/MinerU.gitcd 到 projects/web-api cd projects/web-api在可以科学上网的情况下…

向量外积与秩1矩阵的关系

向量外积与秩1矩阵的关系 flyfish 向量外积是构造秩1矩阵的基本工具&#xff0c;其本质是用两组向量的线性组合刻画矩阵的行和列相关性&#xff1b;任意秩1矩阵必可表示为外积&#xff0c;而低秩矩阵&#xff08;秩 k k k&#xff09;可分解为 k k k 个外积矩阵的和&#x…

设计模式-创建型模式(详解)

创建型模式 单例模式 一个类只允许创建一个对象&#xff0c;称为单例。 单例体现&#xff1a;配置类、连接池、全局计数器、id生成器、日志对象。 懒汉式 (线程不安全) 单例&#xff1a;【不可用】 用到该单例对象的时候再创建。但存在很大问题&#xff0c;单线程下这段代…

什么是BI?有哪些应用场景

BI&#xff08;Business Intelligence&#xff0c;商业智能&#xff09;是通过技术手段对海量业务数据进行采集、整合、分析和可视化的过程&#xff0c;旨在帮助企业从数据中获取洞察&#xff0c;支持决策。其核心是通过工具&#xff08;如Quick BI&#xff09;将原始数据转化为…