深入理解JVM一之解析class文件

目录

  • 深入理解JVM一之解析class文件
    • Java源文件
    • java8编译后的Class源文件
    • 解析依据
    • 最后解析完成
    • 附属
    • 我有话说

深入理解JVM一之解析class文件

Java源文件

package com.luban.ziya.bytecode;

/**
 * Created By ziya
 * 2020/8/5
 */
public class Simple {

    public static final int a = 10;
    static  int b = 10;

    public static void main(String[] args) {
        int b = 10;
        Simple test = new Simple();
    }
}

java8编译后的Class源文件

cafe babe 0000 0034 001e 0a00 0500 1a07
001b 0a00 0200 1a09 0002 001c 0700 1d01
0001 6101 0001 4901 000d 436f 6e73 7461
6e74 5661 6c75 6503 0000 000a 0100 0162
0100 063c 696e 6974 3e01 0003 2829 5601
0004 436f 6465 0100 0f4c 696e 654e 756d
6265 7254 6162 6c65 0100 124c 6f63 616c
5661 7269 6162 6c65 5461 626c 6501 0004
7468 6973 0100 204c 636f 6d2f 6c75 6261
6e2f 7a69 7961 2f62 7974 6563 6f64 652f
5369 6d70 6c65 3b01 0004 6d61 696e 0100
1628 5b4c 6a61 7661 2f6c 616e 672f 5374
7269 6e67 3b29 5601 0004 6172 6773 0100
135b 4c6a 6176 612f 6c61 6e67 2f53 7472
696e 673b 0100 0474 6573 7401 0008 3c63
6c69 6e69 743e 0100 0a53 6f75 7263 6546
696c 6501 000b 5369 6d70 6c65 2e6a 6176
610c 000b 000c 0100 1e63 6f6d 2f6c 7562
616e 2f7a 6979 612f 6279 7465 636f 6465
2f53 696d 706c 650c 000a 0007 0100 106a
6176 612f 6c61 6e67 2f4f 626a 6563 7400
2100 0200 0500 0000 0200 1900 0600 0700
0100 0800 0000 0200 0900 0800 0a00 0700
0000 0300 0100 0b00 0c00 0100 0d00 0000
2f00 0100 0100 0000 052a b700 01b1 0000
0002 000e 0000 0006 0001 0000 0007 000f
0000 000c 0001 0000 0005 0010 0011 0000
0009 0012 0013 0001 000d 0000 0052 0002
0003 0000 000c 100a 3cbb 0002 59b7 0003
4db1 0000 0002 000e 0000 000e 0003 0000
000d 0003 000e 000b 000f 000f 0000 0020
0003 0000 000c 0014 0015 0000 0003 0009
000a 0007 0001 000b 0001 0016 0011 0002
0008 0017 000c 0001 000d 0000 001e 0001
0000 0000 0006 100a b300 04b1 0000 0001
000e 0000 0006 0001 0000 000a 0001 0018
0000 0002 0019 

解析依据

认识字节码文件
一个字节码文件是由这么多部分组成
深入理解JVM一之解析class文件_第1张图片
深入理解JVM一之解析class文件_第2张图片
深入理解JVM一之解析class文件_第3张图片
深入理解JVM一之解析class文件_第4张图片
深入理解JVM一之解析class文件_第5张图片
深入理解JVM一之解析class文件_第6张图片
深入理解JVM一之解析class文件_第7张图片
深入理解JVM一之解析class文件_第8张图片
深入理解JVM一之解析class文件_第9张图片
深入理解JVM一之解析class文件_第10张图片
深入理解JVM一之解析class文件_第11张图片
深入理解JVM一之解析class文件_第12张图片
深入理解JVM一之解析class文件_第13张图片
ASCII对照表
方法指令表

最后解析完成

解析class:

cafe babe  	#cafebabe		majic 魔数
0000	   	#0				minor version 次版本号
0034		#3*16+4 = 52	major version 主版本号,对应 jdk1.8 
001e		#1*16+14 = 30	constant pool count 常量池大小,代表后面几个常量,常量类型有对应

0a 			#10				第一个常量的tag 	--CONSTANT_Methodref_info 
00 05		#5				CONSTANT_Class_info
00 1a   	#1*16+10 = 26   CONSTANT_NameAndType_info 

07     		#07				第二个常量的tag 	--CONSTANT_Class——info 
001b		#1*16+11 = 27	指向全限定名的常量的索引

0a 			#10				第三个常量的tag  	--CONSTANT_Methodref_info
0002		#02				CONSTANT_Class_info
001a		#16+10=26		CONSTANT_NameAndType_info 

09			#09				第四个常量的tag	--CONSTANT_Fieldref_info
0002		#02				CONSTANT_Class_info
001c		#16+12=28		CONSTANT_NameAndType_info 

07			#07				第五个常量的tag	--第二个常量的tag 	--CONSTANT_Class——info
001d		#16+13=29		指向全限定名的常量的索引

01			#01				第六个常量tag		--CONSTANT_Utf-8_info
0001		#01				编码字符串长度		
61			#6*16+1=97->"a"	长度为1的字符串"a"

01			#01				第七个常量tag		--CONSTANT_Utf-8_info
0001		#01
49			#4*16+9=73->"I"	长度为1的字符串"I"
 
	
01			#01				第八个常量tag		--CONSTANT_Utf-8_info
000d		#13				编码字符串长度
436f 6e73 7461 6e74 5661 6c75 65  长度为13的字符串 "ConstantValue"


03			#03				第九个常量tag		--CONSTANT_Integer_info
0000 000a	#10				按高位存储的int值	10

01			#01				第十个常量tag		--CONSTANT_Utf-8_info
0001		#01
62			#6*16+2=98->"b"	长度为1的字符串"b"
 
01			#01				第十一个常量tag		--CONSTANT_Utf-8_info
00 06		#06
3c 696e 6974 3e		#	长度为6的字符串""
 
01			#01				第十二个常量tag		--CONSTANT_Utf-8_info
0003		#03
2829 56		#()V	长度为1的字符串"()V"


01			#01				第十三个常量tag		--CONSTANT_Utf-8_info
0004		#04
436f 6465	#Code	长度为4的字符串"Code"

01			#01				第十四个常量tag		--CONSTANT_Utf-8_info
000f		#15
4c 696e 654e 756d 6265 7254 6162 6c65	#LineNumberTable	长度为15的字符串"LineNumberTable"

01			#01				第十五个常量tag		--CONSTANT_Utf-8_info
0012		#18
4c 6f63 616c 5661 7269 6162 6c65 5461 626c 65	#LocalVariableTable	长度为12的字符串"LocalVariableTable"

01			#01				第十六个常量tag		--CONSTANT_Utf-8_info
0004		#4
7468 6973	this	长度为4的字符串"this"
		
01			#01				第十七个常量tag		--CONSTANT_Utf-8_info
0020		#32
4c 636f 6d2f 6c75 6261 6e2f 7a69 7961 2f62 7974 6563 6f64 652f 5369 6d70 6c65 3b	#Lcom/luban/ziya/bytecode/Simple;	长度为32的字符串"Lcom/luban/ziya/bytecode/Simple;"

01			#01				第十八个常量tag		--CONSTANT_Utf-8_info
0004		#4
6d61 696e	#main	长度为4的字符串"main"

01			#01				第十九个常量tag		--CONSTANT_Utf-8_info
0016		#1*16+6=22 	
28 5b4c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b29 56	#([Ljava/lang/String;)V	长度为22的字符串"([Ljava/lang/String;)V"

01			#01				第二十个常量tag		--CONSTANT_Utf-8_info
0004		#4	
6172 6773	#args	长度为4的字符串"args"

01			#01				第二十一个常量tag		--CONSTANT_Utf-8_info
0013		#19
5b 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b	#[Ljava/lang/String;	长度为19的字符串"[Ljava/lang/String;"

01			#01				第二十二个常量tag		--CONSTANT_Utf-8_info
0004		#4
74 6573 74	#test	长度为4的字符串"test"


01			#01				第二十三个常量tag		--CONSTANT_Utf-8_info
0008		#8
3c63 6c69 6e69 743e	#	长度为4的字符串""


01			#01				第二十四个常量tag		--CONSTANT_Utf-8_info
00 0a		#10
53 6f75 7263 6546 696c 65	#SourceFile	长度为4的字符串"SourceFile"

01			#01				第二十五个常量tag		--CONSTANT_Utf-8_info
00 0b		#11
5369 6d70 6c65 2e6a 6176 61	#Simple.java	长度为4的字符串"Simple.java"


0c 			#12     		第二十六个常量tag		--CONSTANT_NameAndType_info
00 0b		#11				index		指向该字段或者该方法名称常量项的索引
00 0c      	#12				index		指向该字段或方法描述符常量项的索引

01			#01				第二十七个常量tag		--CONSTANT_Utf-8_info
00 1e		#16+14 = 30
63 6f6d 2f6c 7562616e 2f7a 6979 612f 6279 7465 636f 6465 2f53 696d 706c 65	#com/luban/ziya/bytecode/Simple	长度为30的字符串"com/luban/ziya/bytecode/Simple"

0c 			#12     		第二十八个常量tag		--CONSTANT_NameAndType_info
00 0a		#10				index		指向该字段或者该方法名称常量项的索引
00 07      	#7				index		指向该字段或方法描述符常量项的索引


01			#01				第二十九个常量tag		--CONSTANT_Utf-8_info
00 10		#16+0 = 16
6a 6176 612f 6c61 6e67 2f4f 626a 6563 74	#java/lang/Object	长度为4的字符串"java/lang/Object"
========================================================

00 21   	类的访问控制权限

00 02		#2 		类名(索引 2->27="com/luban/ziya/bytecode/Simple")

00 05		#5		父类名(索引 5->29="java/lang/Object")

00 00 		#0 		实现的接口数量
如果实现的接口数量不为0,这里开始为实现的接口数组。否则这里没有直接跳过

00 02		#2 		字段数量

	字段1:
	00 19	access_flags		public static final(这里涉及到 几个基础修饰符的位运算的结果)
	00 06	#6 		name_index(索引6 = "a")
	00 07	#7		descriptor_index(索引7="I",表示int)
	00 01	#1		attributes_count(属性数量)
		attribute_info:
			00 08 		#8 		attribute_name_index (索引8 =ConstantValue)
			00 0000 02	#2		attribute_length;
			00			#0		info
			09			#9		索引(9=10)
	字段2:
	00 08	access_flags		static (这里涉及到 几个基础修饰符的位运算的结果)
	00 0a	#10 		name_index(索引10 = "b")
	00 07	#7		descriptor_index(索引7="I",表示int)
	00 00	#0		attributes_count(属性数量)
===========================================================
00 03  		#3		methods_count 方法数量
methodinfo
	方法1:
	00 01	public		access_flags	
	00 0b 	#11			name_index(索引11="")
	00 0c 	#12			descriptor_index(索引12="()V")
	00 01 	#1			attributes_count
		Code_attribute_info:
			00 0d    	#13				attribute_name_index(索引13=“Code”)	
			00 00002f 	#2*16+15= 47	attribute_length
			00 01		#1 				max_stack;
			00 01		#1				max_locals;
			00 00 00 05 #5				code_length;
			[
			2a  	#aload_0 
			b7 0001	#invokespecial 01(需要两个字节为参数,即后面两个参数)	(索引1=“>”)
			b1 		#return 
			]          对照执行指令翻译

			0000 	#0					exception_table_length;
			如果exception_table_length不为0这里就有 exception_table_info
			0002 	#2					attributes_cout;

			LineNumberTable_attribute
			000e		#14					attribute_name_index(索引14=“LineNumberTable”)
			0000 0006 	#6					attribute_length
			{
				0001 	#01					start_pc
				0000 	#0					line_number
			}	
			0007 	#07		行号

			LocalVariableTable_attribute			
			000f 	#15					attribute_name_index (索引15=“LocalVariableTable”)
			0000 000c 	#12				attribute_length
			{
				0001 	#1			start_pc
				0000 	#0			length;
				
				0005  	#5			name_index;
				0010	#16			descriptor_index “this“;
				
				0011 	#17			index   “Lcom/luban/ziya/bytecode/Simple;”;
			}
			0000 	#0	

	方法2:
	00 09	public static	access_flags	
	00 12 	#18				name_index(索引18="main")
	00 13 	#19				descriptor_index(索引19="([Ljava/lang/String;)V")
	00 01 	#1				attributes_count			
		Code_attribute_info:
			00 0d    	#13				attribute_name_index(索引13=“Code”)	
			00 00 0052 	#5*16+2= 82	attribute_length
			00 02		#2 				max_stack;
			00 03		#3				max_locals;
			00 00 00 0c #12				code_length;   74
			[
			10 	0a 		#bipush 10    			
			3c 			#istore_1
			bb 	0002	#new 2
			59 			#dup
			b7 0003 	#invokespecial 3
			4d 			#store_2
			b1 			#return
			]
			0000 	#0					exception_table_length;
			如果exception_table_length不为0这里就有 exception_table_info 
			0002 	#2 					attributes_count

			LineNumberTable_attribute
			000e		#14					attribute_name_index(索引14=“LineNumberTable”)
			0000 000e 	#14					attribute_length
			{
				0003 	#03					start_pc
				0000 	#0					line_number

				000d 	#13					start_pc
				0003 	#3					line_number

				000e 	#14					start_pc
				000b 	#0					line_number


			}	
			000f 	#15		行号
		    
		    LocalVariableTable_attribute			
			000f 	#15					attribute_name_index (索引15=“LocalVariableTable”)
			0000 0020 	#32				attribute_length
			{
				0003 	#3			start_pc
				0000 	#0			length;
				000c  	#12			name_index;
				0014	#20			descriptor_index "args";
				0015 	#16+5=21	index   “[Ljava/lang/String;”

				0000 	#0			start_pc
				0003 	#3			length;
				0009  	#9			name_index;	
				000a	#10			descriptor_index "b";
				0007 	#7			index   “I”

				0001 	#1			start_pc
				000b 	#11			length;
				0001  	#1			name_index;	
				0016	#22			descriptor_index "test";
				0011 	#17			index   “Lcom/luban/ziya/bytecode/Simple;”
			}
			0002 	#2	
	方法3:
	00 08	static		access_flags	
	00 17 	#23			name_index(索引11="")
	00 0c 	#12			descriptor_index(索引12="()V")
	00 01 	#1			attributes_count
		Code_attribute_info:
			00 0d    	#13				attribute_name_index(索引13=“Code”)	
			00 00001e 	#16+14= 30		attribute_length
			00 01		#1 				max_stack;
			00 01		#1				max_locals;
			00 00 00 06 #6				code_length;
			[
			10 0a 		#bipush 10
			b3	00 04	#putstatic #4 
			b1			#return
			]          对照执行指令翻译

			0000 	#0					exception_table_length;
			如果exception_table_length不为0这里就有 exception_table_info
			0001 	#1					attributes_cout;

			LineNumberTable_attribute
			000e		#14					attribute_name_index(索引14=“LineNumberTable”)
			0000 0006 	#6					attribute_length
			{
				0001 	#01					start_pc
				0000 	#0					line_number
			}	
			000a 	#10		行号
方法结束-----------------------------
attributes开始-----------------------------
0001 		#01 	attributes_count
0018		#24		attribute_name_index(索引24=”SourceFile“)
0000 0002 	#2		attribute_length
00			#0 
19 			#16+9 = 25		"Simple.java"

附属

解析过程中,可以对照idea的jclasslib插件来校验解析是否正确。

我有话说

可能很多人觉得这样自己解析一遍,都是按照表来翻译。有什么意义。我觉得有。最起码知道jvm是怎么将一串16进制的文本。加载成执行指令的。其实jvm加载类的时候。就是这个过程。如果用C++ 来按照这个流程读取这个class文件。不就是类的加载过程吗。这是手写JVM的第一步。

你可能感兴趣的:(JVM,java,jvm,class)