字節碼文章分爲上下兩篇,上篇也就是本文主要講述class文件存在的意義,以及其帶來的益處。並分析其內在構成之一 ———字節碼,而下篇則從指令集方面着手,講解指令集都有哪些,以及其各自表明的含義。最後總結一下Class文件存在的必然性。java
前面說過 Java 虛擬機擁有平臺無關性,但其實如今語言無關性在 JVM 和更加的體現了出來。表現就是目前愈來愈多的語言能夠在 JVM 上運行,而這背後的邏輯,就是這些語言都會被編譯爲 Class 文件,而後在JVM上 運行。jvm
In the future,we will consider bounded extensions to the java virtual machine to provide better support for other languages.ide
上面這段是 JVM 的相關人員提出的願景,所以咱們也對 Java 語言的發展更加的看好。工具
那麼在下面的文章中,咱們就來探討 Class 文件的組成部分,瞭解其內部是如何組織的。3d
首先咱們編寫一個原始的Java源代碼:code
public class TestForEach extends Thread{ private static int ccc = 1; public static void main(String[] args) { int a = 1; int b = 2; int c = a + b; }
這裏咱們使用JDk提供的工具對代碼進行編譯,獲得下面這個二進制流。blog
cafe babe 0000 0034 001d 0a00 0600 0f09 0010 0011 0800 120a 0013 0014 0700 1507 0016 0100 063c 696e 6974 3e01 0003 2829 5601 0004 436f 6465 0100 0f4c 696e 654e 756d 6265 7254 6162 6c65 0100 046d 6169 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 2956 0100 0a53 6f75 .........
對咱們來講,所須要分析的就是這個文件。該二進制流的前4個字節cafe babe
,其被稱爲魔數。它表明了這是一個.class類型的文件,緊接着的第五第六個字節爲次版本號,第七第八個字節爲主版本號,而咱們編譯的這個版本是在 JDK1.8 下。再緊接着就是常量池,訪問標示等信息,由於這些信息計算十分的繁瑣麻煩,在這裏就不展開來計算了,而官方也細心地提供了javap 工具進行反編譯來查看其組織形式。繼承
首先咱們採用javap工具進行反編譯javap -verbose TestForEach
,獲得以下文件接口
Last modified 2019-5-22; size 461 bytes MD5 checksum 2602dfd883d5d5e417e26ce2d42b916d Compiled from "TestForEach.java" public class TestForEach extends java.lang.Thread minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#22 // java/lang/Thread."<init>":()V #2 = Fieldref #5.#23 // TestForEach.d:I #3 = Fieldref #5.#24 // TestForEach.dfinal:I #4 = Fieldref #5.#25 // TestForEach.ccc:I #5 = Class #26 // TestForEach #6 = Class #27 // java/lang/Thread #7 = Utf8 ccc #8 = Utf8 I #9 = Utf8 d #10 = Utf8 dfinal #11 = Utf8 ConstantValue #12 = Integer 2222 #13 = Utf8 <init> #14 = Utf8 ()V #15 = Utf8 Code #16 = Utf8 LineNumberTable #17 = Utf8 main #18 = Utf8 ([Ljava/lang/String;)V #19 = Utf8 <clinit> #20 = Utf8 SourceFile #21 = Utf8 TestForEach.java #22 = NameAndType #13:#14 // "<init>":()V #23 = NameAndType #9:#8 // d:I #24 = NameAndType #10:#8 // dfinal:I #25 = NameAndType #7:#8 // ccc:I #26 = Utf8 TestForEach #27 = Utf8 java/lang/Thread { public TestForEach(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Thread."<init>":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field d:I 9: aload_0 10: sipush 2222 13: putfield #3 // Field dfinal:I 16: return LineNumberTable: line 11: 0 line 17: 4 line 18: 9 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: iconst_2 1: istore_2 2: iconst_1 3: iload_2 4: iadd 5: istore_3 6: return LineNumberTable: line 22: 0 line 23: 2 line 111: 6 static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: iconst_1 1: putstatic #4 // Field ccc:I 4: return LineNumberTable: line 16: 0 } SourceFile: "TestForEach.java"
咱們從頭開始看起,首先是ip
minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER
這裏分別爲Class文件的次版本和主版本號以及訪問標示,版本號前面已經說過了,這裏就很少贅述了,至於訪問標誌,其表明了一些類或接口的訪問信息,如是不是public類型的,有沒有被聲明成final等等。
接着到了 ConstantPool常量池,其能夠簡單理解爲Class文件中的資源倉庫,許多部分都與其關聯。
其中存放了字面量和符號引用兩種類常量。字面量能夠理解爲文本字符串和聲明爲final的常量值等。符號引號則包含如下內容:
上面兩種類型在常量池中都是以表的形式來存儲,其具體含義以下圖所示:
針對常量池內每一個表的含義和咱們獲得的class文件,在這做者分析幾個看一下:
TestForEach
類中定義了int類型的屬性dTestForEach
類,繼承自Thread
ccc,d,dfinal
,全限定名java/lang/Thread
等,方法描述main,()V
等都屬於這一類更多的這裏就不詳細展開了,但常量池中還有一些上面沒有提到的內容,在這裏咱們細說一下.
屬性表集合用於描述某些場景中專有的信息.針對上文出現的屬性表,咱們這裏詳細說下.
本文講了 Class 文件在 Java 達成平臺無關性和語言無關性起到的重要做用,並敘述了Class文件的重要組成部分----文件標識,常量池,屬性集合等。此外還對文件標識和常量池的內容進行了具體的展開描述。
下篇文章則從 JVM 支持的指令集內容開始介紹,並以本文的 Class 文件的指令集集合爲例,具體描述一下指令集的內容。
文章在公衆號"iceWang"第一手更新,有興趣的朋友能夠關注公衆號,第一時間看到筆者分享的各項知識點,謝謝!筆芯。
本系列文章主要借鑑自《深刻分析JavaWeb技術內幕》和《深刻理解Java虛擬機-JVM高級特性與最佳實踐》。