簡介:
Java Virtual Machine JVM 全稱 Java Virtual Machine医吊,也就是我們耳熟能詳?shù)?Java 虛擬機拧烦。它能識別 .class后綴的文件,并且能夠解析它的指令,最終調用操作系統(tǒng)上的函數(shù),完成我們想要的操作上渴。
翻譯:Java 程序不一樣,使用 javac 編譯成 .class 文件之后,還需要使用 Java 命令去主動執(zhí)行它稠氮,操作系統(tǒng)并不認識這些 .class 文件曹阔。所以JVM就是一個翻譯。
image.png
從圖中可以看到隔披,有了 JVM 這個抽象層之后次兆,Java 就可以實現(xiàn)跨平臺了。JVM 只需要保證能夠正確執(zhí)行 .class 文件锹锰,就可以運行在諸如 Linux芥炭、Windows、MacOS 等平臺上了恃慧,JVM講.class和.jar進行翻譯成操作系統(tǒng)能識別的機器碼园蝠,從而調用操作系統(tǒng)的方法。
JVM痢士、JDK,JRE之間的關系:
JVM:只是一個翻譯彪薛,把Class翻譯成機器識別的代碼,JVM 不會自己生成代碼怠蹂,需要大家編寫代碼善延,同時需要很多依賴類庫,這個時候就需要用到JRE城侧。(翻譯)
JRE:它除了包含JVM之外易遣,提供了很多的類庫(就是我們說的jar包,它可以提供一些即插即用的功能嫌佑,比如讀取或者操作文件豆茫,連接網(wǎng)絡,使用I/O等等之類的)這些東西就是JRE提供的基礎類庫屋摇。JVM 標準加上實現(xiàn)的一大堆基礎類庫揩魂,就組成了 Java 的運行時環(huán)境,也就是我們常說的 JRE(Java Runtime Environment)炮温。(提供類庫)
JDK:提供了一些非常好用的小工具火脉,比如 javac(編譯代碼)、java柒啤、jar (打包代碼)倦挂、javap(反編譯<反匯編>)等。這個就是JDK(提供工具)
JVM整體流程圖:
流程圖.png
圖中的運行時數(shù)據(jù)區(qū):jvm所管理的內存白修。
執(zhí)行引擎:將放在運行時數(shù)據(jù)區(qū)的中的方法妒峦,和操作進行解釋執(zhí)行重斑。
解釋執(zhí)行與JIT之間的區(qū)別:
以HelloWord.java為例兵睛,通過javac進行編譯之后得到.class文件,通過ClassLoader進行類加載到運行時數(shù)據(jù)區(qū)中(jvm所管理的內存),jvm再通過執(zhí)行引擎將運行時數(shù)據(jù)區(qū)中的數(shù)據(jù)進行翻譯機器碼祖很,從而執(zhí)行調用操作系統(tǒng)的方法笛丙。那么還是沒有說到解釋執(zhí)行和JIT 呢?
翻譯:
(1)假颇、將所有的類都全部翻譯成機器碼之后才執(zhí)行胚鸯,(速度慢,效率低)
(2)笨鸡、一遍翻譯一遍執(zhí)行姜钳,(相比第一種速度上有了很大的提升)-----解釋執(zhí)行。
那什么又是JIT呢形耗?
在hotspot中哥桥,可能會有熱點方法,如我們同一個方法執(zhí)行很多次激涤,jvm會將這部分代碼直接編程成本地代碼拟糕,提高運行速度,(前提是熱點方法倦踢,熱點類)
運行時數(shù)據(jù)區(qū):
運行時數(shù)據(jù)區(qū)的定義:
Java虛擬機在執(zhí)行Java程序 的過程中會把它所管理的內 存劃分為若干個不同的數(shù)據(jù) 區(qū)域送滞。
運行時數(shù)據(jù)區(qū)的類型:程序計數(shù)器、虛擬機棧辱挥、本 地方法棧犁嗅、Java堆、方法區(qū) (運行時常量池)晤碘、直接內存愧哟。
如圖所示:
image.png
1、程序計數(shù)器:當前線程執(zhí)行的字節(jié)碼的行號指示器哼蛆;各線程之間獨立存儲蕊梧,互不影響。
舉個例子:現(xiàn)有一個Person類腮介,Person中提供了對應的方法肥矢,通過javac編譯之后得到對應的.class文件,現(xiàn)在通過javap -c 對.class 文件進行反編譯叠洗,如圖所示:
image.png
上圖所示是通過反編譯得到.class文件的對應的字節(jié)碼甘改,每行都有對應的編號,在多線程中灭抑,cpu調度線程的十艾,時間輪轉時線程通過程序計數(shù)器記錄當前的對應的行號,直到線程下次擁有cpu調度時接著執(zhí)行腾节,從而確保程序的正常執(zhí)行忘嫉。
2荤牍、虛擬機棧:存儲當前線程運行方法所需的數(shù)據(jù),指令庆冕,返回地址康吵,棧(先進后出)
2.1、棧幀(方法)
2.1.1访递、局部變量表:用來存儲局部變量 只能 存儲8大基本類型晦嵌,對象引用
2.1.2、操作數(shù)棧:存放方法的操作拷姿、執(zhí)行惭载。
2.1.3、動態(tài)鏈表::Java語言特性多態(tài)(需要類運行時才能確定具體的方法)响巢。
2.1.4棕兼、完成出口:正常返回(調用程序計數(shù)器中的地址作為返回)、異常的話(通過異常處理器表<非棧幀中的>來確定)
3抵乓、方法區(qū):
方法區(qū)主要存儲:類信息伴挚,常量,靜態(tài)變量灾炭,即時編譯期編譯后的代碼茎芋。
4、堆:
堆主要存儲:幾乎所有對象實例蜈出,數(shù)組田弥。
方法區(qū)和堆為啥不用一個區(qū)域,要用兩個區(qū)域铡原?堆可以進行頻繁的回收偷厦,方法區(qū)回收比較難,動靜分明燕刻。
從底層深入理解運行時數(shù)據(jù)區(qū):
image.png
1只泼、申請內存,分別給堆卵洗,棧请唱,方法區(qū)分配內存。
2过蹂、類加載十绑,class進入方法區(qū)。
3酷勺、常量本橙,靜態(tài)變量 入方法區(qū)。
4脆诉、虛擬機棧幀--入棧幀
下面先介紹一下HSDB工具甚亭,查看內存的工具:
1)贷币、首先找到jdk的安裝目錄,找到sawindbg.dll文件狂鞋,將改文件復制到對應目錄的jre下。
image.png
2)潜的、現(xiàn)在采用JVMObject.java為例:
public class JVMObject {
public final static String MAN_TYPE = "man"; // 常量
public static String WOMAN_TYPE = "woman"; // 靜態(tài)變量
public static void main(String[] args)throws Exception {//棧幀
Teacher T1 = new Teacher();//堆中 T1 是局部變量
T1.setName("Mark");
T1.setSexType(MAN_TYPE);
T1.setAge(36);
for (int i=0;i<15;i++){//進行15次垃圾回收
System.gc();//垃圾回收
}
Teacher T2 = new Teacher();
T2.setName("King");
T2.setSexType(MAN_TYPE);
T2.setAge(18);
Thread.sleep(Integer.MAX_VALUE);//線程休眠很久很久
}
}
class Teacher{
String name;
String sexType;
int age;//堆
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSexType() {
return sexType;
}
public void setSexType(String sexType) {
this.sexType = sexType;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3)骚揍、在jdk下面的lib目錄找到a-jdi.jar文件,輸入命令如下:
image.png
4)啰挪、讓JVMObject運行信不,再將環(huán)境切換到JVMObject對應的目錄下輸入命令如下所示:
image.png
5)、在HSDB輸入框中輸入JVMObject對應的10756亡呵,并點擊main方法
image.png
image.png
如圖所示為jvm虛擬機的棧區(qū)圖抽活,左邊對應的是內存的地址。
image.png
main方法對應棧幀的內存地址锰什,圖上怎么還會有一個棧幀下硕?原因是在代碼中還調用了sleep方法,查看 sleep源碼屬于本地方法汁胆,驗證了本地方法棧和hotspot棧合并梭姓。
再看class在內存中所在的區(qū)域
image.png
image.png
通過點擊Teacher可以看到有兩個地址,在回看JVMObject代碼嫩码,在代碼中創(chuàng)建了兩個對象誉尖,mark和king對象。為了驗證隨便點擊一個進行查看:
image.png
接下來看看堆空間
image.png
從圖上可以看出铸题,分為兩個空間铡恕,新生代,和老年代丢间,并且內存地址還是連續(xù)的探熔,JVMOject代碼中mark對象通過調用gc進行回收15次,則mark對象進入了老年代烘挫,而king在新生代祭刚。
image.png