深入理解JVM—— HelloWorld字节码完整解析
Posted iaiti
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解JVM—— HelloWorld字节码完整解析相关的知识,希望对你有一定的参考价值。
目录
2、hello代码字节码结构和使用javap -v指令查看字节码结构
3.2 minor_version, major_version
3.3 Constant_pool_count,constant_pool[constant_pool_count-1]
3.3.8 CONSTANT_NameAndType_info
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
public class Test
public static void main(String[] args)
System.out.println("hello");
u2,u4表示占用2个字节,4个字节,
一个字节8个二进制位,也就是2^8,两个16进制的话,是16^2= 2^(4*2)= 2^8次方。
刚好两个16进制解析刚好。
2.1 hello代码字节码结构
2.2 javap -v指令查看字节码结构
3、字节码完整解析
3.1 魔数
cafe babe
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
0000 0034
minor_version, major_version
0000 0034 对应的是java se8
这个版本号的对应,暂时没找到官方文档的对应出处。
不过可以参考这两个引用
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.
通过第一个字节标记常量池类型,接下来我们一个个解析常量池里面的每个元素。
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的解析到这里结束。
对应的是
3.4 access_flags
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
所以对应的是 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
这样就对应上了
这里code的https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html 为系统操作码
0000 exception_table_length
0001 attributes_count
000a 找回constant_pool第十个元素,
01 CONSTANT_Utf8 00 0f 4c 696e 654e756d 6265 7254 6162 6c65 转换后得 LineNumberTable
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
对应
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解析同样可以映射出来
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
3.10 attributes_count
0001 attributes_count
SourceFile_attribute
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
000d 找回constant_pool第十三个元素, SourceFile
00 0000 02
00 0e 找回constant_pool第十四个元素,Test.java
对应的是
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
以上是关于深入理解JVM—— HelloWorld字节码完整解析的主要内容,如果未能解决你的问题,请参考以下文章