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

Posted iaiti

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解JVM—— HelloWorld字节码完整解析相关的知识,希望对你有一定的参考价值。

目录

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

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字节码完整解析的主要内容,如果未能解决你的问题,请参考以下文章

深入理解JVM - 字节码指令

牛逼了,教你用九种语言在JVM上输出HelloWorld

深入理解JVM-java字节码文件结构剖析

耗时一周深入理解JVM虚拟机异常处理字节码性能优化,全网最全面的JVM原理通俗易懂(强烈建议收藏)

深入理解JVM虚拟机5:虚拟机字节码执行引擎

深入理解JVM