Bootstrap

JVM调优篇之JVM基础入门AND字节码文件解读

目录

    • Java程序编译
    • class文件内容
    • 常量池
    • 附录-访问标识表
    • 附录-常量池类型列表

Java程序编译

Java文件通过编译成class文件后,通过JVM虚拟机解释字节码文件转为操作系统执行的二进制码运行。
在这里插入图片描述
规范

Java虚拟机有自己的一套规范,遵循这套规范,任何形式的语言都可以在JVM上运行,前提是编译成class文件,此规范相见oracle官网文档

实现
JVM虚拟机落地实现有很多,常见的有:

  • Hotspot,使用最热门的JVM
  • OpenJ9,IBM开发
  • MicroSoft JVM 微软开发
  • TaobaoVM 淘宝开发
  • azul zing VM 垃圾回收行业标杆,最快

class文件内容

从上图内容可看出class文件包以下几个方面内容:

  • 魔数(magic number)占4个字节,固定的值0xcafebabe,主要来校验class文件。
  • version 版本号,分为小版本号minor version和大版本号major version,各占两个字节。
  • 常量池信息(constant_pool),具体的各种常量信息,字符串,类名,方法名,字段名等。
  • 访问标识(access_flag),2个字节描述类与接口的修饰符,有几个常见的常量值见附录-访问标识
  • 当前类(this_class
  • 当前类的父类(super_class
  • 具体的接口信息(interfaces
  • 具体的属性信息(fileds
  • 具体的方法信息(methods
  • 其它附加属性信息(attributes

更具体的内容如下:

项目Value
Magic Number魔数(CAFE BABE)
Minor Number次版本号
Major Number主版本号
constant_pool_count常量池数量
constant_pool常量池具体实现,是一种表
access_flag访问标识
this_class当前类名
super_class父类名称
interfaces_count接口数量
interfaces具体实现的接口信息
fileds_count属性数量
fileds具体属性信息
methods_count方法数量
methods具体方法的信息
attributes_count其它附加属性的数量
attributes其它附加属性具体信息

现在来看具体示例来看上面的代表是什么意思,准备一个简单的类:

public class ByteCode01 {
    public ByteCode01() {
    }
}

编译之后的字节码文件:

在这里插入图片描述

class文件中就是一个二进制字节流,非0即1,数据类型有五种,u1,u2,u4,u8和_info表类型

上述这种文件需要借助工具来分析具体内容,在idea安装jclasslib插件看Java文件编译成class文件信息,选中编译后的class文件,如下图所示:

在这里插入图片描述
访问标识是固定的值,通过位运算得出的0x0021既代表public又代表super0x00210x00010x0020位与运算),详见附录-访问标识表

更直观的表现:

在这里插入图片描述

常量池

本类索引即当前类名(this_class),cp_infoconstant_pool的表类型,#2是第二张表,cp_info #2代表常量池第二张表,存的是对cp_info #14的引用,cp_info #14存的是具体的字符串值,就是当前类的类名。

在这里插入图片描述
在这里插入图片描述
class文件中都有属于自己的常量池,常量池包含类型列表,来存储类、方法、字段、接口等信息,以及各种字面量,通过解析常量池可以访问这些字符串信息。比如上图出现的CONSTANT_Utf8_infoCONSTANT_Class_info等详见文末常量池类型列表

着重看下CONSTANT_Utf8_info,它用来存储 UTF-8 编码的字符串,相当于一张常量池表,看一下它的具体结构:

字段名字节数描述
tag1CONSTANT_Utf8_info的常量类型是1(其余的见文末常量池类型列表
length2u2(2 字节无符号数) 类型字段,表示 bytes 部分字节长度,以字节为单位的
byteslength实际字符串数据,采用 UTF-8 编码显示

比如

public class Test {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

字符串 Hello, World!,会以 CONSTANT_Utf8_info 类型存储在常量池中,对应的二进制内容或许是:

tag      = 0x01 (标识 CONSTANT_Utf8_info)
length   = 0x000D (字符串的字节长度为 13)
bytes    = 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 ("Hello, World!" 的 UTF-8 编码)

常量池通常分为下面几种常量值类型:

类型标志(tag)描述
CONSTANT_Utf8_info1UTF-8编码的字符串
CONSTANT_Integer_info3整型字面量
CONSTANT_Float_info4浮点型字面
CONSTANT_Long_info5长整型字面量
CONSTANT_Double_info6双精度浮点型字面量
CONSTANT_Class_info7类或接口的符号引用
CONSTANT_String_info8字符串类型字面量
CONSTANT_Fieldref_info9字段的符号引用
CONSTANT_Methodref_info10类中方法的符号引用
CONSTANT_InterfaceMethodref_info11接口中方法的符号引用
CONSTANT_NameAndType_info12字段或方法的部分符号引用
CONSTANT_MethodHandle_info15表示方法句柄
CONSTANT_MethodType_info16表示方法类型
CONSTANT_InvokeDynamic_info18表示一个动态方法调用点

附录-访问标识表

项目Value
ACC_PUBLIC 0x0001是否是public类型
ACC_FINAL 0x0010是否是NULL
ACC_SUPER 0x0020该标志必须为真
ACC_INTERFACE 0x0200是否为接口
ACC_ABSTRACT 0X0400是否是接口或者抽象类
ACC_SYNTHETIC 0x1000编译器自动生成,非用户代码产生
ACC_ANNOTATION 0x2000是否为注解
ACC_ENUM 0x4000是否为枚举

附录-常量池类型列表

常量类型tag值描述
CONSTANT_Utf8_info1用于存储 UTF-8 编码的字符串
CONSTANT_Integer_info3用于存储 4 字节的整型字面量(int
CONSTANT_Float_info4用于存储 4 字节的浮点型字面量(float
CONSTANT_Long_info5用于存储 8 字节的长整型字面量(long
CONSTANT_Double_info6用于存储 8 字节的双精度浮点型字面量(double
CONSTANT_Class_info7用于存储类或接口的符号引用,引用 CONSTANT_Utf8_info 中的类名
CONSTANT_String_info8用于存储字符串字面量,引用 CONSTANT_Utf8_info 中的字符串内容
CONSTANT_Fieldref_info9用于存储字段的符号引用,引用类和字段的描述信息
CONSTANT_Methodref_info10用于存储类中方法的符号引用,引用类和方法的描述信息
CONSTANT_InterfaceMethodref_info11用于存储接口中方法的符号引用,引用接口和方法的描述信息
CONSTANT_NameAndType_info12用于描述字段或方法的名称和类型,引用名称(Utf8)和描述符(Utf8
CONSTANT_MethodHandle_info15用于存储对方法句柄的引用(invokedynamic 指令用)
CONSTANT_MethodType_info16用于存储方法类型的符号引用,指向方法描述符(Utf8
CONSTANT_Dynamic_info17用于存储动态调用点的信息(JDK 11 引入,支持动态常量)
CONSTANT_InvokeDynamic_info18用于存储动态方法调用点的信息,引用引导方法和动态调用名称
CONSTANT_Module_info19用于存储模块的符号引用(JDK 9 引入)
CONSTANT_Package_info20用于存储包的符号引用(JDK 9 引入)
;