深入理解JVM(三)—— HelloWorld字节码完整解析

偏执的太偏执、 2022-12-07 01:23 240阅读 0赞

目录

1、字节码由来

2、hello代码字节码结构和使用javap -v指令查看字节码结构

2.1 hello代码字节码结构

2.2 javap -v指令查看字节码结构

3、字节码完整解析

3.1 魔数

3.2 minor_version, major_version

3.3 Constant_pool_count,constant_pool[constant_pool_count-1]

3.3.1 CONSTANT_Methodref

3.3.2 CONSTANT_Fieldref_info

3.3.3 CONSTANT_String

3.3.4 CONSTANT_Methodref

3.3.5 CONSTANT_Class_info

3.3.6 CONSTANT_Utf8_info

3.3.8 CONSTANT_NameAndType_info

3.3.9 前面出现过的类型,简略对照解析

3.4 access_flags

3.5 this_class

3.6 super_class

3.7 interfaces_count

3.8 fields_count

3.9 method_count

3.9.1 第一个method_info

3.9.1 第二个method_info

3.10 attributes_count

4、总结


1、字节码由来

从一开始学习java的时候,老师就会告诉你这款语言厉害的地方就是,一处编译,处处运行。
上篇jdk和jvm其实都针对不同的操作系统进行了处理。而字节码有自己的规范所在。

那字节码class属于什么文件,文件的类型,计算机文件基本上分为二种,二进制文件和文本文件。如果一个文件专门用于存储文本字符的数据,没有包含字符以外的其他数据,我们就称之为文本文件,除此之外的文件就是二进制文件。

通过官方文档说明我们来看。

A class file consists of a stream of 8-bit bytes. All 16-bit, 32-bit, and 64-bit quantities are constructed by reading in two, four, and eight consecutive 8-bit bytes, respectively.
Java Class文件由8位字节流组成,所有的16位、32位和64位数据分别通过读入2个、4个和8个字节来构造。
class文件中的信息是一项一项排列的, 每项数据都有它的固定长度, 有的占一个字节, 有的占两个字节, 还有的占四个字节或8个字节, 数据项的不同长度分别用u1, u2, u4, u8表示。

https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html 看到美团这篇文章的描述,因为字节码文件由十六进制值组成,而JVM以两个十六进制值为一组,即以字节为单位进行读取。感觉还是怪怪的。并没有说是16进制组成,你用2进制,8进制也能解析,只是说可读性不好。一个字节8个二进制位,也就是2^8,两个16进制的话,是16^2= 2^(4*2)= 2^8次方。

刚好两个16进制解析刚好。

根据上一篇文章:

我们先把整个大体的结构组成切割好,至于为什么这样切割,让我们娓娓道来:

2、hello代码字节码结构和使用javap -v指令查看字节码结构

test.java文件,简单的输出hello

  1. public class Test {
  2. public static void main(String[] args) {
  3. System.out.println("hello");
  4. }
  5. }

u2,u4表示占用2个字节,4个字节,

一个字节8个二进制位,也就是2^8,两个16进制的话,是16^2= 2^(4*2)= 2^8次方。

刚好两个16进制解析刚好。

2.1 hello代码字节码结构

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70

2.2 javap -v指令查看字节码结构

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 1

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 2

3、字节码完整解析

3.1 魔数

cafe babe

20200930160347225.png
The magic item supplies the magic number identifying the class file format; it has the value 0xCAFEBABE.
4字节,魔法数标识这是一个class文件。十六进制值为cafe babe。

3.2 minor_version, major_version

20200930160448450.png

0000 0034

minor_version, major_version
0000 0034 对应的是java se8

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 3
这个版本号的对应,暂时没找到官方文档的对应出处。
不过可以参考这两个引用
https://blogs.oracle.com/darcy/entry/source_target_class_file_version
https://en.wikipedia.org/wiki/Java_class_file#General_layout

3.3 Constant_pool_count,constant_pool[constant_pool_count-1]

#

001d 29 Constant_pool_count

constant_pool[constant_pool_count-1]; 总共28个

Constant_pool_count
001d 计算得29 constant_pool[constant_pool_count-1]; constant_pool数组容量要减一总共28个

The constant_pool is a table of structures (§4.4) representing various string constants, class and interface names, field names, and other constants that are referred to within the ClassFile structure and its substructures. The format of each constant_pool table entry is indicated by its first “tag” byte.
通过第一个字节标记常量池类型,接下来我们一个个解析常量池里面的每个元素。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 4

3.3.1 CONSTANT_Methodref

0a 根据1个字节的tag找到对应的CONSTANT_Methodref结构
第一个constant_pool
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
0a tag
0006 class_index
000f name_and_type_index

3.3.2 CONSTANT_Fieldref_info

CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}

09 CONSTANT_Fieldref 0010 0011

3.3.3 CONSTANT_String

08 CONSTANT_String 00 12

CONSTANT_String_info {
u1 tag;
u2 string_index;
}

3.3.4 CONSTANT_Methodref

0a CONSTANT_Methodref 0013 0014 之前出现的就不再解析了

3.3.5 CONSTANT_Class_info

CONSTANT_Class_info {
u1 tag;
u2 name_index;
}

07 CONSTANT_Class 00 15
07 CONSTANT_Class0016

3.3.6 CONSTANT_Utf8_info

CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}

01 CONSTANT_Utf8 00 06 3c 696e 6974 3e ,第七个constant_pool,标记一下位置,后面有使用
01 CONSTANT_Utf8 0003 2829 56,第八个constant_pool,标记一下位置,后面有使用
01 CONSTANT_Utf8 0004 436f 6465 ,第九个constant_pool,标记一下位置,后面有使用
01 CONSTANT_Utf8 00 0f 4c 696e 654e756d 6265 7254 6162 6c65,第十个constant_pool,标记一下位置,后面有使用
01 CONSTANT_Utf8 00 04 6d 61696e,第十一个constant_pool,标记一下位置,后面有使用
01 CONSTANT_Utf8 0016 285b 4c6a 6176 612f 6c61 6e672f53 7472 696e 673b 2956,第十二个constant_pool,标记一下位置,后面有使用
01 CONSTANT_Utf8 00 0a 53 6f757263 6546 696c 65 ,第十三个constant_pool,标记一下位置,后面有使用
01 CONSTANT_Utf8 0009 5465 7374 2e6a6176 61 ,第十四个constant_pool,标记一下位置,后面有使用

3.3.8 CONSTANT_NameAndType_info

0c CONSTANT_NameAndType_info 0007 0008

CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}

3.3.9 前面出现过的类型,简略对照解析

07 CONSTANT_Class 00 17 同前面
0c CONSTANT_NameAndType_info 0018 0019
01 CONSTANT_Utf8 00 05 68 656c 6c6f
07 CONSTANT_Class 00 1a
0c CONSTANT_NameAndType_info 001b 001c01
01 CONSTANT_Utf8 00 04 54 6573 74
01 CONSTANT_Utf8 0010 6a61 7661 2f6c616e 672f 4f62 6a65 6374
01 CONSTANT_Utf8 00 10 6a 6176612f 6c61 6e67 2f53 7973 7465 6d
01 CONSTANT_Utf8 00036f75 74
01 CONSTANT_Utf8 0015 4c6a 6176 612f 696f 2f507269 6e74 5374 7265 616d 3b
01 CONSTANT_Utf8 0013 6a617661 2f69 6f2f 5072 696e 7453 7472 65616d
01 CONSTANT_Utf8 0007 7072 696e 746c 6e
01 CONSTANT_Utf8 0015 284c6a61 7661 2f6c 616e 672f 5374 7269 6e673b29 56

这里刚好是28个常量池,所以constant_pool的解析到这里结束。

对应的是

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 5

3.4 access_flags

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 6

access_flags
00 21 ACC_PUBLIC|ACC_SUPER

3.5 this_class

00 05 this_class

3.6 super_class

00 06 super_class

3.7 interfaces_count

00 00 interfaces_count

消失的interfaces[interfaces_count] 去哪里了
之前解析到这里就断了,一头雾水,因为按照两个字节算,后面的那些字节码解析就全乱套了。完全解析不下去。
参考了别人的解析之后定位出,如果interfaces_count为0,那么interfaces[interfaces_count]是没有数据,是不占用字节的。

3.8 fields_count

00 00 fields_count
fields[fields_count] 同理

3.9 method_count

00 02 method_count
method_info methods[methods_count];是有两个的

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

3.9.1 第一个method_info

0001 ACC_PUBLIC
00 07 name_index
00 08 descriptor_index
00 01 attributes_count

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

00 09 attribute_name_index
index为9,找回constant_pool第九个元素,
01 CONSTANT_Utf8 0004 436f 6465, 436f 6465十六进制转字符串为Code

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 7

所以对应的是 Code_attribute

00 0000 1d attribute_length

Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

00 01 max_stack
00 01 max_locals
00 0000 05 code_length
(2a b700 01b1) ==code== 2a 为 aload_0 b7为invokespecial 0001 为 #1 b1为return

这样就对应上了

20200930172527170.png

这里code的https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html 为系统操作码

20200930172146964.png

0000 exception_table_length
0001 attributes_count

000a 找回constant_pool第十个元素,
01 CONSTANT_Utf8 00 0f 4c 696e 654e756d 6265 7254 6162 6c65 转换后得 LineNumberTable

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 8

LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}

000a attribute_name_index
0000 0006 attribute_length
0001 line_number_table_length
0000 start_pc
0001 line_number

对应

20200930174309948.png

3.9.1 第二个method_info

0009 ACC_PUBLIC, ACC_STATIC
000b name_index 000c descriptor_index
0001 attributes_count
0009 attribute_name_index 同样和第一个方法也是code
0000 0025 attribute_length

0002 max_stack 0001 max_locals
0000 0009 code_length
(b2 00 02 12 03b6 0004 b1)

b2 getstatic 0002 #2 12 ldc 03 #3 b6 invokespecial 0004 #4 b1 return

这里的opcode解析同样可以映射出来

20200930172634477.png

00 00 exception_table_length
00 01 attributes_count
00 0a attribute_name_index LineNumberTable
0000 000a attributes_length
0002 line_number_table_length 有两个

u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
}
0000 start_pc
0003 line_number

0008 start_pc
0004 line_number

20200930174235913.png

3.10 attributes_count

0001 attributes_count

SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}

000d 找回constant_pool第十三个元素, SourceFile

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 9

00 0000 02
00 0e 找回constant_pool第十四个元素,Test.java

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2lhaXRp_size_16_color_FFFFFF_t_70 10

对应的是

20200930174157427.png

4、总结

当你抱着问题自己一个人无法解决的时候,就会把问题抛出来,和其他人探讨,学习,记录,发掘其他比你更厉害的人。
会发现这个人你以前看过,哦,他也工作5,6年了;哇,这家伙还拿到过海外硕士的学位。但是他学的东西,我现在也在研究。

渐渐的技术的初心又会回来。

中间认识了大飞哥,发现对底层深入了解,且技术的那种热情和乐于助人,着实让我高兴。

翻篇大半,能把整个jvm直接码讲解清楚的也寥寥无几。很多都是写到一半,未完待续。

这几个是我觉得写得比较完整的blog
https://cloud.tencent.com/developer/article/1425357
https://www.jyt0532.com/2020/03/14/epilogue/
https://www.cnblogs.com/binarylei/p/10508441.html

发表评论

表情:
评论列表 (有 0 条评论,240人围观)

还没有评论,来说两句吧...

相关阅读