1.JVM與操作系統(tǒng)的關(guān)系
??JVM全稱Java Virtual Machine(Java虛擬機)。JVM屏蔽了與具體操作系統(tǒng)平臺(Linux、Windows、MacOS等)相關(guān)信息,使Java程序只需要生成JVM上運行的目標代碼(字節(jié)碼),就可以在多種不同平臺上運行。JVM在執(zhí)行字節(jié)碼時,會把字節(jié)碼翻譯成具體平臺上的機器指令(機器語言010101),這就是JVM的跨平臺特征。JVM只識別字節(jié)碼,像Kotlin、Groovy、Jruby等語言,他們也能編譯成字節(jié)碼,所以也能在JVM上跑,這個就是JVM的跨語言特征。
2.Java SE 體系架構(gòu)
JVM只是一個翻譯,在不同操作系統(tǒng)中運行,需要依賴于Java SE體系架構(gòu)
https://docs.oracle.com/javase/8/docs/
3.JVM整體
??.java文件經(jīng)過javac編譯成.class(字節(jié)碼),Java類加載器把字節(jié)碼加載到JVM的運行時數(shù)據(jù)區(qū)(JVM管理的內(nèi)存),執(zhí)行引擎把運行時數(shù)據(jù)區(qū)的數(shù)據(jù)進行執(zhí)行。
運行時數(shù)據(jù)區(qū)
自動化內(nèi)存管理
定義:Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。
類型:程序計數(shù)器、虛擬機棧、本地方法棧、Java堆、方法區(qū)(運行時常量池)、直接內(nèi)存。
線程隔離數(shù)據(jù)區(qū):也叫線程私有區(qū),在jvm中運行多個線程,每個線程都有虛擬機棧、本地方法棧、程序計數(shù)器。
方法區(qū): 放class 、靜態(tài)變量、常量
堆:幾乎所有的對象都在堆中分配
①程序計數(shù)器
指向當前線程正在執(zhí)行的字節(jié)碼指令的地址。
/**
* @author Sun
* 棧幀執(zhí)行對內(nèi)存區(qū)域的影響
*/
public class Test {
public int work(){//在運行過程中,打包一個棧幀
int x=1;//x是一個局部變量
int y=2;
int z=(x+y)*10;
return z;
}
public static void main(String[] args) {
Test test=new Test();
test.work();
}
}
Android studio查看字節(jié)碼:Preferences—>Tools—>External Tools 添加External Tools,添加好后保存。使用:右鍵.java文件—>External Tools—>Show Byte Code。
如圖配置:Name、Program、Arguments、Working directory
Show Byte Code
(這個自己命名就好)
$JDKPath$\bin\javap
-c -verbose $FileClass$
$OutputPath$
我們來看一下上面的work方法的字節(jié)碼片段:
public int work();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: iconst_1 //將int類型1入操作數(shù)棧
1: istore_1 //將操作數(shù)棧中棧頂int類型數(shù)值,存入局部變量表(下標為1的位置)
2: iconst_2 //將int類型2入操作數(shù)棧
3: istore_2 //將操作數(shù)棧中棧頂int類型數(shù)值,存入局部變量表(下標為2的位置)
4: iload_1 //將局部變量表中下標為1的int類型數(shù)據(jù)入棧
5: iload_2 //將局部變量表中下標為2的int類型數(shù)據(jù)入棧
6: iadd //1.將棧頂2個int類型數(shù)值出棧 2.相加 3.將結(jié)果壓入操作數(shù)棧
7: bipush 10 //10的值擴展成int值入操作數(shù)棧
9: imul //1.將棧頂2個int類型數(shù)值出棧 2.相乘 3.將結(jié)果壓入操作數(shù)棧
10: istore_3 //將操作數(shù)棧中棧頂int類型數(shù)值,存入局部變量表(下標為3的位置)
11: iload_3 //將局部變量表中下標為3的int類型數(shù)據(jù)入棧
12: ireturn
...
字節(jié)碼code行號,針對work方法體的偏移量。
簡單理解為:程序計數(shù)器,記錄字節(jié)碼的地址。
為什么需要程序計數(shù)器?時間片輪轉(zhuǎn)機制,線程在執(zhí)行的時候被切出去了,記錄一下執(zhí)行到哪里了。
程序計數(shù)器是JVM內(nèi)存區(qū)域中唯一不會OOM,因為程序計數(shù)器是一塊很小的區(qū)域,只需要記錄地址,int類型就夠了。
②虛擬機棧
存儲當前線程運行方法所需的數(shù)據(jù),指令、返回地址。
每個線程在創(chuàng)建時都會創(chuàng)建一個虛擬機棧,其內(nèi)部保存一個個的棧幀,對應著一次次的Java方法調(diào)用。
棧幀:
在每個Java方法被調(diào)用的時候,都會創(chuàng)建一個棧幀,并入虛擬機棧,一旦完成相應的調(diào)用,則出棧。
每個棧幀包含四個區(qū)域:局部變量表、操作數(shù)棧、動態(tài)連接、返回地址
- 局部變量表:用來存放局部變量的(8大基本數(shù)據(jù)類型和引用類型)
- 操作數(shù)棧:用來存放方法執(zhí)行的操作數(shù)的,參與計算的數(shù)據(jù)會頻繁的入棧和出棧。(基于解釋執(zhí)行)
- 動態(tài)連接:多態(tài),運行時才能確定具體的方法
- 完成出口:正常返回(調(diào)用程序計數(shù)器中的地址作為返回),異常(通過異常處理表來確定)
③本地方法棧
本地方法棧保存的是native方法的信息
當一個JVM創(chuàng)建的線程調(diào)用native方法后,JVM不再為其在虛擬機棧中創(chuàng)建棧幀,JVM只是簡單的動態(tài)鏈接并直接調(diào)用native方法。