JVM 字节码指令解析(下)

本文涉及的产品
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 概述本文主要是基于 .class 文件,进行分析 .class 文件的内容。 这部分个人觉得主要是属于设计机构拓展的内容,大家可以一起来学习一下 Java 字节码的设计结构以及感受一下设计者的设计。

3. 访问标志


Access_Flag 访问标志 访问标识信息包括该Class文件时类和接口是否被定义成了public,是否是 abstract, 如果是类,是否被申明为成final。通过扇面的源代码。


0x 00 21: 表示是0x0020 和0x0001的并集, 表示 ACC_PUBLICACC_SUPER


image.png


4. 类索引、父类索引


00 03  类名,  03 常量池位置  cn/edu/cqvie/jvm/bytecode/TestClass


00 04  父类名.  04 常量池位置 java/lang/Object


00 00 接口个数, 0 个, 接口个数最多 65535


5. 字段表集合


00 01  字段的个数, 这里有一个


image.png

字段表结构


field_info {
  u2 access_flags; 00 02 Fieldref
  u2 name_index; 00 05 (表示字段名称在常量池中的索引位置) m
  u2 descriptor_index; 00 06 (描述符索引) I 
  u2 attributes_count; 00 00
  attribute_info attributes[attributes_count] 
}


6. 方法表集合


00 02 表示有两个方法


image.png


方法表结构


method_info {
  u2 access_flags; 00 01  Methodref
  u2 name_index;  // 00 07  <init>
  u2 descriptor_index; 00 08 // ()V
  u2 attributes_count; 00 01 // 属性结构
  attributes_info attributes[attributes_count]
}


方法属性结构


attribute_info {
  u2 attribute_name_index; 00 09 // Code
  u4 attribute_length; 00 00 00 1D 长度 29
  u1 info[attribute_length];  
  ... 
}


7. 属性表集合


00 09 00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0A 00 00 00 06 00 01 00 00 00 03 00


"Code" 表示下面是执行代码


JVM 预定义了一部分的attribute, 但是编译器自己也可以实现自己的attribute 写入class文件中, 供运行时使用。 不同的attribute 通过attribute_name_index 来区分。


  • JVM 规范预定义的attribute


image.png

  • Code 结构


Code attribute 的作用是保存该放的的结构,如所对应的字节码


Code_attribute {
    u2 attribute_name_index;  // 00 09 ==> Code
    u4 attribute_length; // 00 00 00 1D ==> 29
    u2 max_stack;   //  00 01 栈深度为 1 (栈帧中操作数栈的深度)
    u4 code_length;   // 00 00 00 05 指令的长度是多少
    u1 code[code_lenght]; // 2A B7 00 01 B1 其中 2A
    // 2A aload_0
    // B7 invokespecial
    // 00 #1
    // 01 <java/lang/Object.<init>>
    // B1 return
    // 0 aload_0
    // 1 invokespecial #1 <java/lang/Object.<init>>
    // 4 return
    u2 exception_table_length; // 00 00 
    {
        u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_lenght];
    u2 attributes_count; // 00 01
    attribute_info attributes[attributes_count];
}


  • attribute_length 表示 attribute 所包含的字节数, 不包含attribute_name_index 和 attribute_length 字段。


  • max_stack 表示这个方法运行的任何时刻锁能达到的操作数栈的最大深度。


  • max_locals 表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。


  • code_length 表示该方法所包含的字节码的字节数以及具体的指令码。


  • 具体字节码即是该方法被调用时,虚拟机所执行的字节码。


  • exception_table, 这里存放的是处理异常信息。


  • 每个 exception_table, 这里存放的是处理异常的信息。


  • 每个 exception_table 表项由start_pc , end_pc, handler_pc, catch_type 组成。
  • start_pc 和 end_pc 表示在code数组中的从 start_pc 到 end_pc 处(包含start_pc, 不包含end_pc)的指令抛出的异常会由 这个表项来处理。
  • handler_pc 表示处理异常的代码的开始处。catch_type 表示会被处理的异常类型, 它指向常量池里的一个异常类。 当catch_type为0时, 表示处理所有的异常。
  • LineNumberTable 的结构


LineNumberTable_attribute {
    u2 attribute_name_index; //00 0A 常量池10号位置  LineNumberTable
    u4 attribute_lenght;  // 00 00 00 06 一共 6个长度
    u2 line_number_table_length;  // 00 01 映射的对数  1 对
    line_number_info {
        u2 start_pc; // 指令行号  00 00 
        u2 line_number; // 源码调试  00 03
    }
    line_number_table[line_number_table_length]; 
}


局部变量表 LocalVariableTable


LocalVariableTable_attribute {
    u2 attribute_name_index; 
    u4 attribute_lenght;
    u2 local_variable_table_length; 
    {
        u2 start_pc;
        u2 line_number;
        u2 name_index;
        u2 descriptor_index; 
        u2 index;
    }
}


总结


  1. 构造方法中会初始化成员属性的默认值,如果自己实现了默认的构造方法, 依然还是在构造方法中赋值,这就是对指令的重排序。


  1. 如果多个构造方法那么每个构造方法中都有初始化成员变量的属性,来保障每个构造方法初始化的时候都会执行到属性的初始化过程。


  1. 如果构造方法中有执行语句, 那么会先执行赋值信息, 然后在执行自定义的执行代码。


  1. 对于Java每一个实例方法(非静态方法), 其在编译后生成的字节码中比实际方法多一个参数, 它位于方法的第一个参数位置. 我们就可以在当前方法中 的this去访问当前对象中的this这个操作是在Javac 编译器在编译期间将this的访问转换为对普通实例方法的参数访问,接下来在运行期间, 由JVM的调用实例方法时, 自动向实例方法中传入该this参数, 所以在实例方法的局部变量表中, 至少会一个指向当前对象的局部变量。


  1. 字节码对于处理异常的方式:


  • 统一采用异常表的方式来对异常处理。


  • 在Jdk1.4.2之前的版本中, 并不是使用异常表的方式来对异常进行处理的,而是采用特定的指令方式。


  • 当异常处理存在finally 语句块时,现代化的JVM才去的处理方式将finally语句块的自己吗拼接到每一个catch块后面, 换句话说,程序中中存在多个catch块,就会在每一个catch块后面重复多少个finally 的语句块字节码。


相关文章
|
5天前
|
安全 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法的深度融合
在Java的演进历程中,Lambda表达式无疑是Java 8引入的一项革命性特性,它极大地简化了函数式编程在Java中的应用,使得代码更加简洁、易于阅读和维护。而这一切的背后,JVM的invokedynamic指令功不可没。本文将深入探讨invokedynamic指令的工作原理及其与Java Lambda语法的紧密联系,带您领略这一技术背后的奥秘。
9 1
|
8天前
|
C# 开发者 Windows
震撼发布:全面解析WPF中的打印功能——从基础设置到高级定制,带你一步步实现直接打印文档的完整流程,让你的WPF应用程序瞬间升级,掌握这一技能,轻松应对各种打印需求,彻底告别打印难题!
【8月更文挑战第31天】打印功能在许多WPF应用中不可或缺,尤其在需要生成纸质文档时。WPF提供了强大的打印支持,通过`PrintDialog`等类简化了打印集成。本文将详细介绍如何在WPF应用中实现直接打印文档的功能,并通过具体示例代码展示其实现过程。
37 0
|
12天前
|
缓存 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法
【8月更文挑战第27天】在Java的演进历程中,invokedynamic指令的引入和Lambda表达式的出现无疑是两大重要里程碑。它们不仅深刻改变了Java的开发模式和性能表现,还极大地推动了Java在函数式编程和动态语言支持方面的进步。本文将从技术角度浅析JVM中的invokedynamic指令及其与Java Lambda语法的紧密联系。
28 0
|
15天前
|
运维 监控 Java
【JVM 调优秘籍】实战指南:JVM 调优参数全解析,让 Java 应用程序性能飙升!
【8月更文挑战第24天】本文通过一个大型在线零售平台的例子,深入探讨了Java虚拟机(JVM)性能调优的关键技术。面对应用响应延迟的问题,文章详细介绍了几种常用的JVM参数调整策略,包括堆内存大小、年轻代配置、垃圾回收器的选择及日志记录等。通过具体实践(如设置`-Xms`, `-Xmx`, `-XX:NewRatio`, `-XX:+UseParallelGC`等),成功降低了高峰期的响应时间,提高了系统的整体性能与稳定性。案例展示了合理配置JVM参数的重要性及其对解决实际问题的有效性。
34 0
|
18天前
|
存储 Java 测试技术
解析 -XX:+UseCompressedOops JVM 选项
【8月更文挑战第21天】
31 0
|
18天前
|
存储 Java 索引
|
18天前
|
存储 Java 索引
64 位 JVM 中 int 的大小解析
【8月更文挑战第21天】
26 0
|
10天前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
1月前
|
存储 NoSQL Redis
redis 6源码解析之 object
redis 6源码解析之 object
52 6
|
17天前
|
测试技术 Python
python自动化测试中装饰器@ddt与@data源码深入解析
综上所述,使用 `@ddt`和 `@data`可以大大简化写作测试用例的过程,让我们能专注于测试逻辑的本身,而无需编写重复的测试方法。通过讲解了 `@ddt`和 `@data`源码的关键部分,我们可以更深入地理解其背后的工作原理。
16 1

推荐镜像

更多