龙空技术网

Java 如何执行字节码?一文解析!

博文小火柴 122

前言:

而今小伙伴们对“java项目执行”可能比较讲究,我们都需要学习一些“java项目执行”的相关资讯。那么小编在网摘上收集了一些对于“java项目执行””的相关知识,希望咱们能喜欢,同学们快快来学习一下吧!

Windows 操作系统上编译的 Java 程序,不经过修改就能够直接在 Linux 操作系统上运行;与之对比的是 C 语言,在 Linux 平台编译的 C 程序,一般情况下如果不进行特殊的转换,那么是不能在 Windows 操作系统上运行的。

要了解 Java 是如何实现这一目标的,我们需要对 Java实际的运行做一个简单的介绍。首先 Java 的源程序的扩展名是.java,经过编译程序编译之后生成扩展为.class 的字节码文件。

本文分析class文件的格式,描述 Java 如何执行字节码,并通过 ASM 工具动态生成属性访问工具类,它类似 ReflectASM,是一个高性能反射处理工具。

一、基础知识:

在.class 文件中,类名使用的都是全限定名,并且其表示方式与源文件中的方式不一致,比如 java.lang.String 在 String.class 中的表示方式就是 java/lang/String。

Java 虚拟机的操作基于两种数据类型:基本类型和引用类型。

1.基本类型:包括数字类型、boolean 类型和 returnAddress,其中 returnAddress 在 Java 语言中没有对应类型。

2.引用类型:包括 class、array、interface,引用类型是与实例关联的。这些类型在.class 文件中有不同的描述,如下表所示。

引用类型举例,如下表所示。

方法的描述是参数类型描述与返回类型描述的组合,参数类型描述在一对()之间,后边紧跟返回类型的描述。我们以 java.lang.Object 与 java.lang.String 中的几个方法举例,如下表所示。

二、.class文件的格式

.class 文件的格式(类似 C 语言)如下:

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];}

classFile 是二进制字节流,以 8 位二进制数据为基础构成,虽然可以区分一个个数据项,但其结构是按顺序线性排列的,各个数据项中间没有其他的分隔符。

在 class 文件的结构中,只有两种数据类型无符号数。其中 u1、u2、u4 分别代表无符号 1 个字节、2 个字节、4 个字节,而且其多字节的排列是“大端法”,即高位字节在低位。 “表”是由多个无符号数或其他表项构成的,比如 cp_info 表示的就是常量池表。

1.Magic:魔数,4 个字节,固定为 0xCAFEBABE。

2.minor_version、major_version:分别占 2 个字节,表示子版本号和主版本号,用于 Java 虚拟机识别是否支持该.class 文件,以及是否支持新特性等。

3.constant_pool_count:2 个字节,其表示的值为常量池的实际大小+1。

4.constant_pool[]:常量池,其中包含各种格式的常量,包括类的全限定名、字段名称和描述符、方法名称和描述符等,其通用格式为 cp_info{u1 tag;u1 info[]},由于篇幅有限,不再详细展开介绍常量池结构。我们知道 Java 类的所有常量、类名、方法名等字符串都存放在常量池中。

5.access_flags:2 个字节,表示该类或接口的访问标志,比如 ACC_PUBLIC(值为 0x0001)表示 public,ACC_FINAL 表示 final(值为 0x0010),因此 0x0011 表示 final public。

6.this_class:2 个字节,表示当前类,其值为常量池中的索引,该位置所表示的常量类型必须为 0x07,即 class 类型。

7.super_class:2 个字节,表示当前类的直接父类,其值为常量池中的索引。该位置所表示的常量类型必须为 0x07,即 class 类型,当然在 Object 类的.class 文件中,该值为 0x0000。

8.interfaces_count:2 个字节,表示当前类或接口直接实现的接口的数量。

9.interfaces[]:表示直接实现的接口,其值为常量池中索引的位置,且类型必须为 0x07。

10.interfaces_count:2 个字节,表示当前类或接口直接实现的接口的数量。

11.interfaces[]:表示直接实现的接口,其值为常量池中索引的位置,且类型必须为 0x07。

12.methods_count:2 个字节,表示该类的方法表中方法结构的数量。

13.methods[]:方法表,表示该类中的所有方法,包括实例方法、类方法、初始化方法等,不包括从父类或父接口中继承但没实现的方法。

14.attributes_count:2 个字节,表示该.class 文件的属性表中实体的数量。

15.attributes[]:属性表,此位置的属性表示的是.class 文件中的属性,存储在此位置的属性是有限值的。

字段的结构如下:

field_info {     u2                 access_flags;     u2                 name_index;     u2                 descriptor_index;     u2                 attributes_count;     attribute_info attributes[attributes_count];}

方法的结构如下:

method_info {        u2                access_flags;        u2                name_index;        u2                descriptor_index;        u2                attributes_count;        attribute_info attributes[attributes_count];}

属性的结构如下:

attribute_info {      u2 attribute_name_index;      u4 attribute_length;      u1 info[attribute_length];}

我们看到 Class、Filed、Method,甚至在 Attribute 中都有属性结构,但不同的位置拥有的属性是不同的,比如 SourceFile 属性存储在 ClassFile 中,Code 属性存储在 Method 结构中,而存储字节码对应 Java 源码行号的 LineNumberTable 和描述本地变量表中变量与 Java 源码变量对应关系的 LocalVariableTable 属性则存储在 Code 属性中,用于 Java Debug。

内容摘自《高性能Java系统权威指南》第九章

李家智 著

本书特点:

内容上,总结作者从事Java开发20年来在头部IT企业的高并发系统经历的真实案例,极具参考意义和可读性。

对于程序员和架构师而言,Java 系统的性能优化是一个超常规的挑战。这是因为 Java 语言和 Java 运行平台,以及 Java 生态的复杂性决定了 Java 系统的性能优化不再是简单的升级配置或者简单的 “空间换时间”的技术实现,这涉及 Java 的各种知识点。

本书从高性能、易维护、代码增强以及在微服务系统中编写Java代码的角度来描述如何实现高性能Java系统,结合真实案例,让读者能够快速上手实战。

风格上本书的风格偏实战,读者可以下载书中的示例代码并运行测试。读者可以从任意一章开始阅读,掌握性能优化知识为公司的系统所用。

本书适合:

中高级程序员和架构师;

以及有志从事基础技术研发、开源工具研发的极客阅读;

也可以作为 Java 笔试和面试的参考书。

标签: #java项目执行