一個java文件是怎麼一步一步執行的

說點什麼呢,java比你想的要難

 

寫了多年java,發現好多人並不知道一個class文件怎麼被解析執行的,所以我也發表下看法

1.  編寫java源文件

2.  把java源文件編譯成.class字節碼文件,JVM不認識源文件

 3.  JVM處理class文件

搞java開發,不得不提的就是JVM了,JVM全稱是Java Virtual Machine(簡稱JVM,中文叫Java虛擬機,請務必記住JVM,看到不少人整天JVM的,都不知道它的全稱是什麼),java的宿主環境,可以認爲JVM就是虛擬仿真出來的一臺計算機。簡單繪了一張圖,如下(一圖勝千言):

java之所以一次編寫,到處運行,就是因爲虛擬機(虛擬虛擬,虛擬出來的計算機,一臺被託管的電腦)的緣故。

 

3.1 jvm處理class文件

 加載是指將java源文件編譯之後的class文件讀入到內存中,然後在堆區創建一個java.lang.Class對象,用於封裝類在方法區內的數據結構。類加載的最終目的是封裝類在方法區的數據結構,並向java程序員提供訪問方法區數據的接口。

類的生命週期一共分爲5個階段,加載、連接、初始化、使用、卸載。

加載:類的加載過程主要完成3件事件,1.通過類的全限定名來獲取定義此類的二進制字節流,2.將這個類字節流代表的靜態存儲結構轉爲方法區的運行時數據結構,3.在堆中生成一個代表此類的java.lang.Class對象,作爲訪問方法區這些數據結構的入口。這個過程主要是類加載器完成的,如圖

 

從上圖我們就可以看出類加載器之間的父子關係和管轄範圍。

BootStrap是最頂層的類加載器,它是由C++編寫而成,並且已經內嵌到JVM中了,主要用來讀取Java的核心類庫JRE/lib/rt.jar

ExtensionClassLoader是是用來讀取Java的擴展類庫,讀取JRE/lib/ext/*.jar

AppClassLoader是用來讀取CLASSPATH指定的所有jar包或目錄的類文件

甚至可以自定義加載器

加載過程用到了很牛掰的雙親委派模型,它是這樣的一套機制:

"類加載器"加載類時,先判斷該類是否已經加載過了;

如果還未被加載,則首先委託其"類加載器"的"父類加載器"去加載該類,這是一個向上不斷搜索的過程,當類所有的"祖宗類加載器"(包括了bootstrap  classloader)都沒有加載到類,則回到發起者"類加載器"去加載,如果還加載不了,則拋出ClassNotFoundException.

請參考http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/ClassLoader.java

連接:這個過程分3個階段(校驗,準備,解析)完成。首先是校驗,此階段主要校驗class文件包含的信息是否符合jvm的規範。具體的校驗通過對文件格式,元數據,字節碼,符號引用驗證來完成。然後是準備,此階段爲類變量分配內存,並將其初始化爲默認值。最後是解析,即把類型中的符號引用轉換成爲直接引用。具體的解析有4種,1.類或接口的解析,2.字段解析,3.類方法解析,4.接口方法解析。完成這3個階段就完成了類的連接。

初始化(很重要):即執行類的構造器方法的過程。有5種方法可以完成初始化:1.調用new方法,2.使用Class類的newInstance方法(反射機制),3.使用Constructor類的newInstance方法(反射機制),4.使用Clone方法創建對象,5.使用(反)序列化機制創建對象

碼農開發用new關鍵字創建對象,而框架(spring,mybatis等)特別喜歡用第2和3種方式創建對象,還記得我說的框架四要素嗎,其中有一要素就是反射機制

使用:完成類的初始化後,就可以對類進行實例化,在程序中進行使用了

卸載:當類被加載,連接和初始化後,它的生命週期就始了,當代表類的class對象不在被引用時,class對象就會結束生命週期,類在方法區內的數據就會被卸載。因此一個類何時結束生命,取決於代表它的class對象何時結束生命。

 

3.2  JVM的內存結構

Java程序在運行時,需要在內存中的分配空間。爲了提高運算效率,就對數據進行了不同空間的劃分,因爲每一片區域都有特定的處理數據方式和內存管理方式,如上圖,Java中的內存分配了5個區:

 Method Area方法區

方法區是被所有線程共享,所有字段和方法字節碼,以及一些特殊方法如構造函數,接口代碼也在此定義。簡單說,所有定義的方法的信息都保存在該區域,此區域屬於共享區間。

靜態變量+常量+類信息+運行時常量池存在方法區中,實例變量存在堆內存中。

 Heap 堆:堆這塊區域是JVM中最大的,應用的對象和數據都是存在這個區域,這塊區域也是線程共享的,也是 gc 主要的回收區,一個 JVM 實例只存在一個堆類存,堆內存的大小是可以調節的。類加載器讀取了類文件後,需要把類、方法、常變量放到堆內存中,以方便執行器執行。

注jdk8永久代改爲了metaspace元空間

Stack 棧:棧也叫棧內存,主管Java程序的運行,是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對於棧來說不存在垃圾回收問題,只要線程一結束該棧就Over,生命週期和線程一致,是線程私有的, 基本類型的變量和對象的引用變量都是在函數的棧內存中分配。遵循“先進後出”/“後進先出”原則。

PC Register程序計數器

每個線程都有一個程序計算器,就是一個指針,指向方法區中的方法字節碼(下一個將要執行的指令代碼),由執行引擎讀取下一條指令,是一個非常小的內存空間,幾乎可以忽略不記。

Native Method Stack本地方法棧

它的具體做法是Native Method Stack中登記native方法,在Execution Engine執行時加載native libraies。

 

暫且這麼多,以後有時間了接着補充。。。。。。

 

參考:

0. The Java® Language Specification(8)  https://docs.oracle.com/javase/specs/jls/se8/html/index.html

1. The Java® Virtual Machine Specification (8) https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

我想於java碼農而言,沒有比這這兩個官方上千頁的規範更重要的了,看清楚了:一個是Java語言規範,一個是Java虛擬機規範

當然還有其他規範爲了方便碼農開發

 

 

2. Advanced Java Bytecode Tutorial  https://www.jrebel.com/blog/java-bytecode-tutorial

3.  深入JVM:ClassLoader相關知識簡介   https://developer.51cto.com/art/201009/227269.htm

4. Java Class Loader  https://javapapers.com/core-java/java-class-loader/

5. Understanding the Java ClassLoader  https://www.ibm.com/developerworks/java/tutorials/j-classloader/j-classloader.html

6. Advanced Java Class Tutorial: A Guide to Class Reloading  https://www.toptal.com/java/java-wizardry-101-a-guide-to-java-class-reloading

7. 看完這篇文章你還敢說你懂JVM嗎? https://virtual.51cto.com/art/201901/591418.htm?mobile

8. JVM  internals  https://blog.jamesdbloom.com/JVMInternals.html#jvm_system_threads

9.  Difference between initializing a class and instantiating an object? 

https://stackoverflow.com/questions/15074083/difference-between-initializing-a-class-and-instantiating-an-object

10. class-loader-subsystem-jvm-internals  https://codepumpkin.com/class-loader-subsystem-jvm-internals/

 

參考書籍:

0.  Java編程思想  (第1,,2,5,8,14章節)

 

 1.  Virtual Machines

抽象虛擬模仿一臺計算機

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章