JVM实战与原理---Class文件结构
JVM實(shí)戰(zhàn)與原理
目錄
Class文件結(jié)構(gòu)
1. 數(shù)據(jù)結(jié)構(gòu)
2. class文件結(jié)構(gòu)詳解
2.1 魔數(shù)與Class文件的版本
2.2 常量池
2.3 訪問標(biāo)志
2.4 類索引、父類索引與接口索引集合
2.5 字段表集合
2.6 方法表集合
2.7?屬性表集合
Class文件結(jié)構(gòu)
章節(jié)目的:介紹class文件的結(jié)構(gòu)
引言:當(dāng)使用javac命令對(duì)java文件進(jìn)行編譯后,便生成了Class文件,那么Class文件的結(jié)構(gòu)是怎么樣的呢?
下面是Class文件結(jié)構(gòu)的介紹。
1. 數(shù)據(jù)結(jié)構(gòu)
首先,我們需要知道Class文件的數(shù)據(jù)結(jié)構(gòu)如下,其中u1、u2、u4分別代表1個(gè)字節(jié)、2個(gè)字節(jié)、4個(gè)字節(jié),info結(jié)尾的代表一個(gè)復(fù)合結(jié)構(gòu)的數(shù)據(jù)
ClassFile {u4 magic;u2 minor_version;u2 major_version;u2 constant_pool_count;cp_info constant_pool[constant_pool_count-1];u2 access_flags;u2 this_class;u2 super_class;u2 interfaces_count;u2 interfaces[interfaces_count];u2 fields_count;field_info fields[fields_count];u2 methods_count;method_info methods[methods_count];u2 attributes_count;attribute_info attributes[attributes_count]; }2. class文件結(jié)構(gòu)詳解
接著,我們繼續(xù)通過以下這段java代碼來(lái)分析
class Person {private static String name = "wenxl";public static void main(String[] args) {System.out.println(name);} }對(duì)于已經(jīng)編譯生成了class文件,此時(shí)將class文件通過winHex打開,會(huì)有如下十六進(jìn)制的字節(jié)碼,例如首個(gè)CA就為一個(gè)字節(jié),即為u1。
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1500000000 CA FE BA BE 00 00 00 34 00 22 0A 00 07 00 13 09 漱壕 4 " 00000016 00 14 00 15 09 00 06 00 16 0A 00 17 00 18 08 00 00000032 19 07 00 1A 07 00 1B 01 00 04 6E 61 6D 65 01 00 name 00000048 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 Ljava/lang/Stri 00000064 6E 67 3B 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 ng; <init> ( 00000080 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 )V Code Line 00000096 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 04 6D 61 NumberTable ma 00000112 69 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E in ([Ljava/lan 00000128 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 08 3C 63 g/String;)V <c 00000144 6C 69 6E 69 74 3E 01 00 0A 53 6F 75 72 63 65 46 linit> SourceF 00000160 69 6C 65 01 00 0B 50 65 72 73 6F 6E 2E 6A 61 76 ile Person.jav 00000176 61 0C 00 0A 00 0B 07 00 1C 0C 00 1D 00 1E 0C 00 a 00000192 08 00 09 07 00 1F 0C 00 20 00 21 01 00 05 77 65 ! we 00000208 6E 78 6C 01 00 06 50 65 72 73 6F 6E 01 00 10 6A nxl Person j 00000224 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 ava/lang/Object 00000240 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 java/lang/Syst 00000256 65 6D 01 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 em out Ljava 00000272 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B /io/PrintStream; 00000288 01 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 java/io/Print 00000304 53 74 72 65 61 6D 01 00 07 70 72 69 6E 74 6C 6E Stream println 00000320 01 00 15 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 (Ljava/lang/S 00000336 74 72 69 6E 67 3B 29 56 00 20 00 06 00 07 00 00 tring;)V 00000352 00 01 00 0A 00 08 00 09 00 00 00 03 00 00 00 0A 00000368 00 0B 00 01 00 0C 00 00 00 1D 00 01 00 01 00 00 00000384 00 05 2A B7 00 01 B1 00 00 00 01 00 0D 00 00 00 *? ? 00000400 06 00 01 00 00 00 01 00 09 00 0E 00 0F 00 01 00 00000416 0C 00 00 00 26 00 02 00 01 00 00 00 0A B2 00 02 & ? 00000432 B2 00 03 B6 00 04 B1 00 00 00 01 00 0D 00 00 00 ? ? ? 00000448 0A 00 02 00 00 00 06 00 09 00 07 00 08 00 10 00 00000464 0B 00 01 00 0C 00 00 00 1E 00 01 00 00 00 00 00 00000480 06 12 05 B3 00 03 B1 00 00 00 01 00 0D 00 00 00 ? ? 00000496 06 00 01 00 00 00 03 00 01 00 11 00 00 00 02 00 00000512 122.1 魔數(shù)與Class文件的版本
ClassFile {u4 magic;u2 minor_version;u2 major_version;....... }對(duì)應(yīng)的字節(jié)碼為:CA FE BA BE 00 00 00 34
作用:每個(gè)Class文件的頭4個(gè)字節(jié)稱為魔數(shù)magic,作用是確定這個(gè)文件是否為JVM能接受的Clcass文件,固定為0xCAFEBABE。
后4字節(jié)說明Class文件的版本
字節(jié)碼詳解:00 00 00 34 說明該文件版本為JDK1.8。
2.2 常量池
ClassFile {.......u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1];....... }對(duì)應(yīng)的字節(jié)碼為:00 22
作用:前兩個(gè)字節(jié)說明常量池的數(shù)量,后面則是常量池內(nèi)容。常量池可以理解是Class文件的資源倉(cāng)庫(kù)。
字節(jié)碼詳解:其中前兩個(gè)字節(jié)0x0022轉(zhuǎn)換為十進(jìn)制為34,說明有33個(gè)常量
cp_info常量類型的數(shù)據(jù)結(jié)構(gòu)為
cp_info {u1 tag; u1 info[]; }其中tag的值代表當(dāng)前常量屬于那種常量類型
| Tag Value | Constant Type | DESC | DATA STRUCTURE |
| 1 | CONSTANT_Utf8 | utf-8編碼的字符串 | CONSTANT_Utf8_info { ??? u1 tag; ??? u2 length; ??? u1 bytes[length]; } |
| 3 | CONSTANT_Integer | 整形 | CONSTANT_Integer_info { ??? u1 tag; ??? u4 bytes; } |
| 4 | CONSTANT_Float | 浮點(diǎn)型 | CONSTANT_Float_info { ??? u1 tag; ??? u4 bytes; } |
| 5 | CONSTANT_Long | 長(zhǎng)整形 | CONSTANT_Long_info { ??? u1 tag; ??? u4 high_bytes; ??? u4 low_bytes; } |
| 6 | CONSTANT_Double | 雙精度浮點(diǎn)型 | CONSTANT_Double_info { ??? u1 tag; ??? u4 high_bytes; ??? u4 low_bytes; } |
| 7 | CONSTANT_Class | 類或接口的符號(hào)引用 | CONSTANT_Class_info { ??? u1 tag; ??? u2 name_index; } |
| 8 | CONSTANT_String | 字符串類型 | CONSTANT_String_info { ??? u1 tag; ??? u2 string_index; } |
| 9 | CONSTANT_Fieldref | 字段的符號(hào)引用 | CONSTANT_Fieldref_info { ??? u1 tag; ??? u2 class_index; ??? u2 name_and_type_index; } |
| 10 | CONSTANT_Methodref | 類中方法的符號(hào)引用 | CONSTANT_Methodref_info { ??? u1 tag; ??? u2 class_index; ??? u2 name_and_type_index; } |
| 11 | CONSTANT_InterfaceMethodref | 接口中方法的符號(hào)引用 | CONSTANT_InterfaceMethodref_info { ??? u1 tag; ??? u2 class_index; ??? u2 name_and_type_index; } |
| 12 | CONSTANT_NameAndType | 字段或方法的部分符號(hào)引用 | CONSTANT_NameAndType_info { ??? u1 tag; ??? u2 name_index; ??? u2 descriptor_index; } |
| 15 | CONSTANT_MethodHandle | 表示方法句柄 | CONSTANT_MethodHandle_info { ??? u1 tag; ??? u1 reference_kind; ??? u2 reference_index; } |
| 16 | CONSTANT_MethodType | 標(biāo)識(shí)方法類型 | CONSTANT_MethodType_info { ??? u1 tag; ??? u2 descriptor_index; } |
| 18 | CONSTANT_InvokeDynamic | 表示一個(gè)動(dòng)態(tài)方法調(diào)用點(diǎn) | CONSTANT_InvokeDynamic_info { ??? u1 tag; ??? u2 bootstrap_method_attr_index; ??? u2 name_and_type_index; } |
我們按字節(jié)順序開始分析,第一個(gè)tag值為0x0A,對(duì)應(yīng)常量類型為CONSTANT_Methodref,其結(jié)構(gòu)為
CONSTANT_Methodref_info {u1 tag;u2 class_index;u2 name_and_type_index; }對(duì)應(yīng)的字節(jié)碼為
0A00 0700?13
0007為class_index指向一個(gè)CONSTANT_Class_info
0013為name_and_type_index指向一個(gè)CONSTANT_NameAndType
那指向的這兩個(gè)又是什么呢?我們往下繼續(xù)分析
第二個(gè)tag值為0x09,對(duì)應(yīng)常量類型為CONSTANT_Fieldref,其結(jié)構(gòu)為
CONSTANT_Fieldref_info {u1 tag;u2 class_index;u2 name_and_type_index; }對(duì)應(yīng)的字節(jié)碼為
0900 1400 15
0014為class_index指向一個(gè)CONSTANT_Class_info
0015為name_and_type_index指向一個(gè)CONSTANT_NameAndType
我們按這樣的邏輯將常量池字 節(jié)碼解析為
| Index | Tag | Info | Tag Value |
| 1 | 0A | 0007 0013 | CONSTANT_Methodref |
| 2 | 09 | 0014 0015 | CONSTANT_Fieldref |
| 3 | 09 | 0006 0016 | CONSTANT_Fieldref |
| 4 | 0A | 0017 0018 | CONSTANT_Methodref |
| 5 | 08 | 0019 | CONSTANT_String |
| 6 | 07 | 001A | CONSTANT_Class |
| 7 | 07 | 001B | CONSTANT_Class |
| 8 | 01 | 0004 6E6D 6561 | CONSTANT_Utf8 |
| 9 | 01 | 0012 4C.. ..3B | CONSTANT_Utf8 |
| 10 | 01 | 0006 3C.. ..3E | CONSTANT_Utf8 |
| 11 | 01 | 0003 2829 56 | CONSTANT_Utf8 |
| 12 | 01 | 0004 436F 6465 | CONSTANT_Utf8 |
| 13 | 01 | 000F 4C.. ..65 | CONSTANT_Utf8 |
| 14 | 01 | 0004 6D61 696E | CONSTANT_Utf8 |
| 15 | 01 | 0016 28.. ..56 | CONSTANT_Utf8 |
| 16 | 01 | 0008 3C.. ..3E | CONSTANT_Utf8 |
| 17 | 01 | 000A 53.. ..65 | CONSTANT_Utf8 |
| 18 | 01 | 000B 50.. ..61 | CONSTANT_Utf8 |
| 19 | 0C | 000A 000B | CONSTANT_NameAndType |
| 20 | 07 | 001C | CONSTANT_Class |
| 21 | 0C | 001D 001E | CONSTANT_NameAndType |
| 22 | 0C | 0008 0009 | CONSTANT_NameAndType |
| 23 | 07 | 001F | CONSTANT_Class |
| 24 | 0C | 0020 0021 | CONSTANT_NameAndType |
| 25 | 01? | 0005 77.. ..6C | CONSTANT_Utf8 |
| 26 | 01? | 0006 50.. ..6E | CONSTANT_Utf8 |
| 27 | 01? | 0010 6A.. ..74 | CONSTANT_Utf8 |
| 28 | 01? | 0010 6A.. ..6D | CONSTANT_Utf8 |
| 29 | 01? | 0003 6F75 74 | CONSTANT_Utf8 |
| 30 | 01? | 0015 4C.. ..3B | CONSTANT_Utf8 |
| 31 | 01? | 0013 6A.. ..6D | CONSTANT_Utf8 |
| 32 | 01? | 0007 70.. ..6E | CONSTANT_Utf8 |
| 33 | 01? | 0015 28.. ..56 | CONSTANT_Utf8 |
這時(shí)我們?cè)倩氐降谝粋€(gè)CONSTANT_Methodref,該常量用來(lái)描述的方法,其中
0007為class_index指向一個(gè)CONSTANT_Class_info,說明是哪個(gè)類的方法
0013為name_and_type_index指向一個(gè)CONSTANT_NameAndType,說明的是這個(gè)方法的方法名與方法的入?yún)⒓胺祷刂殿愋?/p>
0007即index為7的常量,我們可以看到是
| 7 | 07 | 001B | CONSTANT_Class |
CONSTANT_Class的結(jié)構(gòu)為
CONSTANT_Class_info {u1 tag;u2 name_index; }同理,index為001B的即第27個(gè)常量是
| 27 | 01? | 0010 6A.. ..74 | CONSTANT_Utf8 |
CONSTANT_Utf8的結(jié)構(gòu)為
CONSTANT_Utf8_info {u1 tag;u2 length;u1 bytes[length]; }length為0010,即后面跟著長(zhǎng)度為16的字符串,6A.. ..74轉(zhuǎn)換為對(duì)應(yīng)的字符串為java/lang/Object,說明調(diào)用的是java/lang/Object的方法
同樣,0013指向第19個(gè)常量,是個(gè)CONSTANT_NameAndType_info
| 19 | 0C | 000A 000B | CONSTANT_NameAndType |
結(jié)構(gòu)如下
CONSTANT_NameAndType_info {u1 tag;u2 name_index;u2 descriptor_index; }000A指向第10個(gè)常量,轉(zhuǎn)換為對(duì)應(yīng)的字符串為<init>,說明方法名為<init>
| 10 | 01 | 0006 3C.. ..3E | CONSTANT_Utf8 |
000B指向第11個(gè)常量,轉(zhuǎn)換為對(duì)應(yīng)的字符串為()V,說明入?yún)榭?#xff0c;返回值類型是void。
?
我們可以用javap -verbose Person的命令反編譯,就可以得到j(luò)avap工具幫我們解析的字節(jié)碼描述,我們發(fā)現(xiàn)其中Constant pool部分與我們分析出的結(jié)果是一致的。
Classfile /D:/用戶目錄/我的文檔/Code/JVMSearch/Person.classLast modified 2020-12-19; size 513 bytesMD5 checksum dea507ac0ecc8c9063c778c6f748a2bbCompiled from "Person.java" class Personminor version: 0major version: 52flags: ACC_SUPER Constant pool:#1 = Methodref #7.#19 // java/lang/Object."<init>":()V#2 = Fieldref #20.#21 // java/lang/System.out:Ljava/io/PrintStream;#3 = Fieldref #6.#22 // Person.name:Ljava/lang/String;#4 = Methodref #23.#24 // java/io/PrintStream.println:(Ljava/lang/String;)V#5 = String #25 // wenxl#6 = Class #26 // Person#7 = Class #27 // java/lang/Object#8 = Utf8 name#9 = Utf8 Ljava/lang/String;#10 = Utf8 <init>#11 = Utf8 ()V#12 = Utf8 Code#13 = Utf8 LineNumberTable#14 = Utf8 main#15 = Utf8 ([Ljava/lang/String;)V#16 = Utf8 <clinit>#17 = Utf8 SourceFile#18 = Utf8 Person.java#19 = NameAndType #10:#11 // "<init>":()V#20 = Class #28 // java/lang/System#21 = NameAndType #29:#30 // out:Ljava/io/PrintStream;#22 = NameAndType #8:#9 // name:Ljava/lang/String;#23 = Class #31 // java/io/PrintStream#24 = NameAndType #32:#33 // println:(Ljava/lang/String;)V#25 = Utf8 wenxl#26 = Utf8 Person#27 = Utf8 java/lang/Object#28 = Utf8 java/lang/System#29 = Utf8 out#30 = Utf8 Ljava/io/PrintStream;#31 = Utf8 java/io/PrintStream#32 = Utf8 println#33 = Utf8 (Ljava/lang/String;)V {Person();descriptor: ()Vflags:Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: getstatic #3 // Field name:Ljava/lang/String;6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V9: returnLineNumberTable:line 6: 0line 7: 9static {};descriptor: ()Vflags: ACC_STATICCode:stack=1, locals=0, args_size=00: ldc #5 // String wenxl2: putstatic #3 // Field name:Ljava/lang/String;5: returnLineNumberTable:line 3: 0 } SourceFile: "Person.java"2.3 訪問標(biāo)志
ClassFile {.......u2 access_flags;....... }對(duì)應(yīng)的字節(jié)碼為:00 20
作用:用于識(shí)別類或者接口層次的訪問信息
字節(jié)碼詳解:每個(gè)位所代表的意義如下表,因?yàn)樵擃愂荍DK1.2之后的編譯器進(jìn)行編譯,且其他標(biāo)識(shí)均為0,故為00 20。大家可以試試在類被聲明為public,abstract,final的情況下,字節(jié)碼是如何展示的。
| Flag Name | Value | Interpretation |
| ACC_PUBLIC | 0x0001 | 是否為public類型 |
| ACC_FINAL | 0x0010 | 是否被聲明為final,只有類可設(shè)置 |
| ACC_SUPER | 0x0020 | 是否允許使用invokespecial字節(jié)碼指令的新語(yǔ)意,JDK1.0.2該指令語(yǔ)意發(fā)生改變,故JDK1.0.2后該標(biāo)志都為真 |
| ACC_INTERFACE | 0x0200 | 標(biāo)識(shí)這是一個(gè)接口 |
| ACC_ABSTRACT | 0x0400 | 是否為abstract類型 |
| ACC_SYNTHETIC | 0x1000 | 標(biāo)識(shí)這個(gè)類并非由用戶代碼產(chǎn)生 |
| ACC_ANNOTATION | 0x2000 | 標(biāo)識(shí)這是一個(gè)注解 |
| ACC_ENUM | 0x4000 | 標(biāo)識(shí)這是一個(gè)枚舉 |
2.4 類索引、父類索引與接口索引集合
ClassFile {.......u2 this_class;u2 super_class;u2 interfaces_count;u2 interfaces[interfaces_count];....... }對(duì)應(yīng)的字節(jié)碼為:00 06 00 07 00 00
作用:這三項(xiàng)數(shù)據(jù)確定了這個(gè)類的繼承關(guān)系,類索引(this_class)確定類的全限定名,父類索引(super_class)確定類的父類的全限定名,接口索引集合(interfaces)描述類實(shí)現(xiàn)了哪些接口
字節(jié)碼詳解:00 06指向常量池第6個(gè)常量,類型為CONSTACT_Class_info,值為Person。
00 07同樣類型為CONSTACT_Class_info,值為java/lang/Object。
00 00說明該類沒實(shí)現(xiàn)任何接口。好奇的同學(xué)可以試試實(shí)現(xiàn)了接口后,這里的字節(jié)碼是如何展示的。
2.5 字段表集合
ClassFile {.......u2 fields_count;field_info fields[fields_count];....... }對(duì)應(yīng)的字節(jié)碼為:00 01?00 0A?00 08 00 09?00 00
作用:描述接口或者類中的聲明的變量。
字節(jié)碼詳解:
其中前兩個(gè)字節(jié)0x0001轉(zhuǎn)換為十進(jìn)制為1,說明有1個(gè)字段。
field_info常量類型的數(shù)據(jù)結(jié)構(gòu)為
field_info {u2 access_flags;u2 name_index;u2 descriptor_index;u2 attributes_count;attribute_info attributes[attributes_count]; }access_flag為字段訪問標(biāo)志,含義表如下。字節(jié)碼為00 0A,說明ACC_PRIVATE和ACC_STATIC為真,即該字段為private和static
| Flag Name | Value | Interpretation |
| ACC_PUBLIC | 0x0001 | 字段是否public |
| ACC_PRIVATE | 0x0002 | 字段是否private |
| ACC_PROTECTED | 0x0004 | 字段是否protected |
| ACC_STATIC | 0x0008 | 字段是否static |
| ACC_FINAL | 0x0010 | 字段是否final |
| ACC_VOLATILE | 0x0040 | 字段是否volatile |
| ACC_TRANSIENT | 0x0080 | 字段是否transient |
| ACC_SYNTHETIC | 0x1000 | 字段是否由編譯器自動(dòng)產(chǎn)生的 |
| ACC_ENUM | 0x4000 | 字段是否enum |
name_index代表字段的簡(jiǎn)單名稱,descriptor_index代表字段和方法的描述符。
字節(jié)碼為00 08 00 09,指向常量池第8和第9個(gè)常量,類型為CONSTACT_Utf8_info,值分別為name和Ljava/lang/String;。
說明該字段名為name,描述符為java/lang/String。
attribute_info則是該字段的屬性表,字節(jié)碼為00 00, 說明沒有屬性。
2.6 方法表集合
ClassFile {.......u2 methods_count;method_info methods[methods_count];....... }對(duì)應(yīng)的字節(jié)碼為:00 03?00 00?00 0A 00 0B 00 01
作用:描述接口或者類中的方法。
字節(jié)碼詳解:
其中前兩個(gè)字節(jié)0x0003轉(zhuǎn)換為十進(jìn)制為3,說明有3個(gè)方法。
method_info常量類型的數(shù)據(jù)結(jié)構(gòu)為
method_info {u2 access_flags;u2 name_index;u2 descriptor_index;u2 attributes_count;attribute_info attributes[attributes_count]; }access_flag為字段訪問標(biāo)志,含義表如下。字節(jié)碼為00 00,說明標(biāo)志均為假
| Flag Name | Value | Interpretation |
| ACC_PUBLIC | 0x0001 | 方法是否public |
| ACC_PRIVATE | 0x0002 | 方法是否private |
| ACC_PROTECTED | 0x0004 | 方法是否protected |
| ACC_STATIC | 0x0008 | 方法是否static |
| ACC_FINAL | 0x0010 | 方法是否final |
| ACC_SYNCHRONIZED | 0x0020 | 方法是否synchronized |
| ACC_BRIDGE | 0x0040 | 方法是否是由編譯器產(chǎn)生的橋接方法 |
| ACC_VARARGS | 0x0080 | 方法是否接受不定參數(shù) |
| ACC_NATIVE | 0x0100 | 方法是否native |
| ACC_ABSTRACT | 0x0400 | 方法是否abstract |
| ACC_STRICT | 0x0800 | 方法是否strictfp |
| ACC_SYNTHETIC | 0x1000 | 方法是否是由編譯器自動(dòng)產(chǎn)生的 |
字節(jié)碼為00 0A 00 0B,指向常量池第10和第11個(gè)常量,類型為CONSTACT_Utf8_info,值分別為<init>和()V。name_index代表方法名,descriptor_index代表方法的描述符。
說明該方法名為<init>,描述符為()V。
attribute_info則是該字段的屬性表,那么屬性表是什么樣的結(jié)構(gòu)呢。
2.7?屬性表集合
ClassFile {u2 attributes_count;attribute_info attributes[attributes_count];....... }對(duì)應(yīng)的字節(jié)碼為:00 01?00 0C?00 00 00 1D
作用:Class文件、字段表、方法表都可以攜帶自己的屬性表集合,用于描述某些場(chǎng)景專有的信息
字節(jié)碼詳解:
其中前兩個(gè)字節(jié)0x0001轉(zhuǎn)換為十進(jìn)制為1,說明有1個(gè)屬性。
attribute_info常量類型的數(shù)據(jù)結(jié)構(gòu)為
attribute_info {u2 attribute_name_index;u4 attribute_length;u1 info[attribute_length]; }attribute_name_index為屬性名。字節(jié)碼為00 0C,指向常量池第12個(gè)常量,類型為CONSTACT_Utf8_info,值為Code,說明這是一個(gè)Code屬性。
00 00 00 1D說明后面屬性內(nèi)容的長(zhǎng)度,1D為十進(jìn)制的29,說明后面29個(gè)字節(jié)的內(nèi)容為屬性內(nèi)容。那么這個(gè)Code屬性的屬性作用和結(jié)構(gòu)內(nèi)容是怎么樣的呢?
Code屬性作用:方法中的代碼經(jīng)過編譯器變?yōu)榈淖止?jié)碼指令,存儲(chǔ)在Code屬性內(nèi)。
Code屬性表結(jié)構(gòu):
字節(jié)碼詳解:00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01?00 0D
這里從max_stack開始,max_stack描述了操作數(shù)棧深度的最大值,這里是00 01說明最大值為1
max_locals描述了局部變量表所需的存儲(chǔ)空間,單位是Slot,對(duì)于double和long這兩種64位的需要兩個(gè)Slot存放,其余的byte、char、float、int、short、boolean和returnAddress等長(zhǎng)度不超過32位的則用1個(gè)Slot存放。
Slot可以被重用,當(dāng)代碼執(zhí)行超過局部變量的作用域,該變量占用的Slot可以被其他Slot占用,所以max_locals不等于方法用到了多少個(gè)局部變量,而是編譯器根據(jù)變量的作用域計(jì)算出max_locals的大小。
code_length和code存儲(chǔ)Java源程序編譯后生成的字節(jié)碼指令,code_length為00 05,說明后面有5個(gè)字節(jié)為字節(jié)碼指令,為2A B7 00 01 B1,根據(jù)指令表,2A對(duì)應(yīng)指令為aload_0,含義是將第0個(gè)Slot中為reference類型的本地變量推送到操作數(shù)棧頂;B7對(duì)應(yīng)指令為invokespecial,作用是以棧頂?shù)膔eference類型的數(shù)據(jù)所指向的對(duì)象作為方法接收者,調(diào)用對(duì)象的實(shí)例構(gòu)造方法,后跟兩個(gè)字節(jié)是他的參數(shù),說明調(diào)用哪一個(gè)方法;00 01是參數(shù),指向常量池第1個(gè)常量,類型為CONSTACT_Methodref_info,值為java/lang/Object."<init>":()V;B1對(duì)應(yīng)指令為return,含義是返回此方法,且返回值為void,執(zhí)行該條指令后,方法結(jié)束。
exception_table用于實(shí)現(xiàn)Java一場(chǎng)及finally處理機(jī)制,00 00說明定義了0個(gè)異常。
最后的是attribute_info,說明Code屬性的屬性,00 01說明有1個(gè)屬性,結(jié)構(gòu)是一樣的。
字節(jié)碼為00 0D,指向常量池第13個(gè)常量,類型為CONSTACT_Utf8_info,值為L(zhǎng)ineNumberTable,說明這是一個(gè)LineNumberTable屬性。
LineNumberTable屬性作用:描述Java源碼行號(hào)與字節(jié)碼行號(hào)之間的對(duì)應(yīng)關(guān)系
LineNumberTable屬性表結(jié)構(gòu):
字節(jié)碼詳解:00 00 00 06 00 01 00?00 00 01
00 00 00 06說明后面6個(gè)字節(jié)為屬性描述。
line_number_table_length說明line_number_table長(zhǎng)度,00 01說明后續(xù)有1個(gè)line_number_table_length。
line_number_table包含兩個(gè)數(shù)據(jù)項(xiàng),其中start_pc是字節(jié)碼行號(hào),line_number是Java源碼行號(hào)。
00?00 00 01說明行號(hào)數(shù)據(jù)是0:1。
還有很多屬性,如下。
每個(gè)屬性都有不同的結(jié)構(gòu)及作用,讀者可以自行修改源碼,從而發(fā)現(xiàn)不同的屬性。
| 屬性名稱 | 使用位置 | 含義 |
| Code? | 方法表 | Java代碼編譯成的字節(jié)碼指令 |
| ConstantValue | 字段表 | final關(guān)鍵字定義的常量值 |
| Deprecated | 類、方法表、字段表 | 被聲明為deprecated的方法和字段 |
| Exceptions | 方法表 | 方法拋出的異常 |
| EnclosingMethod | 類文件 | 僅當(dāng)一個(gè)類為局部類或者匿名類時(shí)才能擁有這個(gè)屬性,這個(gè)屬性用于標(biāo)識(shí)這個(gè)類所在的外圍方法 |
| InnerClasses | 類文件 | 內(nèi)部類列表 |
| LineNumberTable | Code屬性 | Java源碼的行號(hào)與字節(jié)碼指令的對(duì)應(yīng)關(guān)系 |
| LocalVariableTable | Code屬性 | 方法的局部變量描述 |
| StackMapTable | Code屬性 | JDK1.6新增屬性,供新的類型檢查驗(yàn)證器檢查和處理目標(biāo)方法的局部變量和操作數(shù)棧所需要的類型是否匹配 |
| Signature | 類、方法表、字段表 | 用于支持泛型情況下的方法簽名 |
| SourceFile | 類文件 | 記錄源文件名稱 |
| SourceDebugExtension | 類文件 | 存儲(chǔ)額外的調(diào)試信息 |
| Synthetic | 類、方法表、字段表 | 標(biāo)識(shí)方法或字段為編譯器自動(dòng)生成的 |
| LocalVariableTypeTable | 類 | 使用特征簽名代替描述符,是為了引入泛型語(yǔ)法之后能描述泛型參數(shù)化類型而添加 |
| RuntimeVisibleAnnotations | 類、方法表、字段表 | 為動(dòng)態(tài)注解提供支持,指明哪些注解是運(yùn)行時(shí)可見的 |
| RuntimeInvisibleAnnotations | 類、方法表、字段表 | 指明哪些注解是運(yùn)行時(shí)不可見的 |
| RuntimeVisibleParameterAnnotations | 方法表 | 與RuntimeVisibleAnnotations類似,作用對(duì)象為方法參數(shù) |
| RuntimeInvisibleParameterAnnotations | 方法表 | 與RuntimeInvisibleAnnotations類似,作用對(duì)象為方法參數(shù) |
| AnootationDefault | 方法表 | 記錄注解類元素的默認(rèn)值 |
| BootstrapMethods | 類文件 | 保存invokedynamic指令引用的引導(dǎo)方法限定符 |
后面的字節(jié)碼也無(wú)需在分析下去了,后面還有兩個(gè)方法,一個(gè)屬性,按照這個(gè)結(jié)構(gòu)繼續(xù)解析下去,就可以發(fā)現(xiàn),跟javap反編譯出來(lái)的描述是一致的。
Classfile /D:/用戶目錄/我的文檔/Code/JVMSearch/Person.classLast modified 2020-12-19; size 513 bytesMD5 checksum dea507ac0ecc8c9063c778c6f748a2bbCompiled from "Person.java" class Personminor version: 0major version: 52flags: ACC_SUPER Constant pool:#1 = Methodref #7.#19 // java/lang/Object."<init>":()V#2 = Fieldref #20.#21 // java/lang/System.out:Ljava/io/PrintStream;#3 = Fieldref #6.#22 // Person.name:Ljava/lang/String;#4 = Methodref #23.#24 // java/io/PrintStream.println:(Ljava/lang/String;)V#5 = String #25 // wenxl#6 = Class #26 // Person#7 = Class #27 // java/lang/Object#8 = Utf8 name#9 = Utf8 Ljava/lang/String;#10 = Utf8 <init>#11 = Utf8 ()V#12 = Utf8 Code#13 = Utf8 LineNumberTable#14 = Utf8 main#15 = Utf8 ([Ljava/lang/String;)V#16 = Utf8 <clinit>#17 = Utf8 SourceFile#18 = Utf8 Person.java#19 = NameAndType #10:#11 // "<init>":()V#20 = Class #28 // java/lang/System#21 = NameAndType #29:#30 // out:Ljava/io/PrintStream;#22 = NameAndType #8:#9 // name:Ljava/lang/String;#23 = Class #31 // java/io/PrintStream#24 = NameAndType #32:#33 // println:(Ljava/lang/String;)V#25 = Utf8 wenxl#26 = Utf8 Person#27 = Utf8 java/lang/Object#28 = Utf8 java/lang/System#29 = Utf8 out#30 = Utf8 Ljava/io/PrintStream;#31 = Utf8 java/io/PrintStream#32 = Utf8 println#33 = Utf8 (Ljava/lang/String;)V {Person();descriptor: ()Vflags:Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: getstatic #3 // Field name:Ljava/lang/String;6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V9: returnLineNumberTable:line 6: 0line 7: 9static {};descriptor: ()Vflags: ACC_STATICCode:stack=1, locals=0, args_size=00: ldc #5 // String wenxl2: putstatic #3 // Field name:Ljava/lang/String;5: returnLineNumberTable:line 3: 0 } SourceFile: "Person.java"?
總結(jié)
以上是生活随笔為你收集整理的JVM实战与原理---Class文件结构的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL中concat函数(连接字符串
- 下一篇: JVM实战与原理---类加载机制