一.Java從編譯到運作的過程
1.圖示(JDK,JRE,JVM三者關系)
java
檔案經過
javac
的編譯後形成了
class
位元組碼檔案,然後送入JVM在JRE的類庫的幫助下進行類加載。從這張圖也可以看出JDK,JRE,JVM之間的關系。
2.三者關系
- JVM: JVM隻是一個翻譯,把Class翻譯成機器識别的代碼,但是需要注意,JVM 不會自己生成代碼,需要大家編寫代碼,同時需要很多依賴類庫,這個時候就需要用到JRE。
- JRE: JRE是什麼,它除了包含JVM之外,還提供了很多的類庫(就是我們說的jar包,它可以提供一些即插即用的功能,比如讀取或者操作檔案,連接配接網絡,使用I/O等等之類的)這些東西就是JRE提供的基礎類庫。JVM 标準加上實作的一大堆基礎類庫,就組成了 Java 的運作時環境,也就是我們常說的 JRE(Java Runtime Environment)。
- JDK: 但對于程式員來說,JRE還不夠。我寫完要編譯代碼,還需要調試代碼,還需要打包代碼、有時候還需要反編譯代碼。是以我們會使用JDK,因為JDK還提供了一些非常好用的小工具,比如
(編譯代碼)、javac
(反編譯)等。這個就是JDK。javap
二.JVM是一種規範
為什麼說JVM是一種規範呢?有以下兩個原因
①JVM的跨平台性
同一個Java程式,可以在多個不同的作業系統上執行,不受平台的影響(當然前提是你安裝了屬于此平台的JDK)。比如安裝了windows版本的JDK,我的Java程式就可以在windows作業系統上面運作。
②JVM的語言無關性
JVM識别的就是class檔案,不管你是什麼語言,Java也好,kotlin也好,還是你自己創的語言也好,隻要能編譯成class檔案,JVM就能識别并進行操作。
綜上,我們說JVM是一種規範。
常見的JVM實作有
我們用的一般就是Hotspot
下面我們看一下JVM是怎麼規範的
三.JVM對記憶體的規範
JVM在執行Java程式的過程中會把它所管理的記憶體劃分為若幹個不同的資料區域,也就是對記憶體進行規範化。我們把進行規範化的這塊區域叫做運作時資料區域。裡面包含的内容如圖所示
根據是否為線程共享,分成兩部分。
一部分是線程共享區,裡面有方法區(方法區在hotspot中有時被稱為永久代、元空間)和堆,另一部分是線程私有區,裡面有虛拟機棧,本地方法棧,程式計數器。
除了運作時資料區域之外,還有沒經過虛拟化的直接記憶體,也就是沒經過規範化的記憶體。這部分也可以被Java程式所使用。比如電腦記憶體是16G,JVM占用了5G,那麼剩下的11G就被稱為直接記憶體。但是這部分使用起來不太友善。
1.虛拟機棧講解
①概念
存儲目前線程運作java方法所需的資料,指令,傳回位址。有棧幀這個概念,一個方法就是一個棧幀。
②執行個體示範
比如這一段程式,是在
main
方法中
A
調用
B
,然後
B
調用
C
。那麼在程式執行的過程中,虛拟機棧發生的變化是這樣的。
首先main方法是主線程,我們命名為線程1,執行main方法的時候,main方法會作為一個棧幀,進入虛拟機棧内
然後main方法調用A方法,A方法作為另一個棧幀進入虛拟機棧
以此類推
虛拟機棧還可以進行大小設定,預設值取決于平台,具體怎麼設定不用管。
這樣的話就掌握了虛拟機棧和棧幀的概念了
2.棧幀裡面有什麼
一般來講有四個,分别是局部變量表,操作數棧,動态連接配接(這個先不管),完成出口。
- 先解釋下程式計數器吧,它指向目前線程正在執行的位元組碼指令的位址。
我們換一張圖,繼續對棧幀進行細緻的講解
示範程式如下
這一部分示範程式非常簡單,就不再解釋了。我們之前說,JVM針對的是位元組碼檔案即class檔案,是以我們展示下上述代碼編譯後形成的class檔案
我們隻看
work
方法的紅框部分
最左邊的數字,後面帶個冒号的那些數字,是位元組碼的位址,也就是位元組碼的偏移量,程式計數器儲存的就是這個值。比如程式計數器儲存着1,那麼下一步就執行
istore_1
這個指令。OK,下面讓我們來詳細示範下這個過程。
①iconst_1、istore_1
後面兩步和前兩步一樣.
②iconst_2、istore_2
③然後執行iload_1、iload_2
④iadd
⑤bipush 10
⑥imul
這個也是分了兩步
後面兩步就是把這個30存到局部變量表,然後又取出來,最後通過完成出口完成,并傳回這個30。
完成出口是調用此方法的那個位置,比如這裡是在
main
方法的第三行調用的此方法
那麼完成出口可能就記3,然後方法傳回的時候就通過3來傳回到方法調用的位置,繼續向下執行
OK,以上就是通過一段程式,來對棧幀裡面的結構進行的介紹。
3.本地方法棧
本地方法棧跟 Java 虛拟機棧的功能類似,Java 虛拟機棧用于管理 Java 函數的調用,而本地方法棧則用于管理本地方法的調用。但本地方法并不是用 Java 實作的,而是由 C 語言實作的(比如
Object.hashcode
方法)。
本地方法棧是和虛拟機棧非常相似的一個區域,它服務的對象是
native
方法。你甚至可以認為虛拟機棧和本地方法棧是同一個區域。HotSpot直接把本地方法棧和虛拟機棧合二為一 。
4.運作時資料區中的其他區域
除了剛剛講的線程私有區和直接記憶體,還有方法區和堆。那麼一段程式執行後,某些代碼在哪些區域是怎麼劃分的呢?看下面的代碼
-
和age
都是靜态變量或者常量,這些是跟随着類的,會在方法區裡面。sex
- 後兩個
和object
是成員變量,是跟随着對象的,等對象isKing
出來之後他們才有,而對象是在堆裡面new
的,是以他們兩個也在堆裡面。new
-
方法裡面的main
和x
還有y
都是局部變量,會在局部變量表裡面。lobject
四.深入了解JVM記憶體處理
執行個體代碼
它運作的時候,記憶體處理的流程是
-
①JVM申請記憶體
②初始化運作時資料區
③類加載
④執行方法
⑤建立對象
前三步完成以後,會是這個樣子
最後建立對象,是這樣子
那麼對象建立了,如何進行回收呢?下篇文章将會詳細講解垃圾回收機制,現在我們隻需要知道,堆可以分成兩部分,新生代和老年代,新生代又可以分成三部分