什么是JVM
JVM
是Java Virtual Machine
(Java虛擬機(jī)
)的縮寫(xiě)膀曾,JVM
是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī)拐纱,是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的于毙。
Java 虛擬機(jī) (JVM)是提供運(yùn)行時(shí)環(huán)境來(lái)驅(qū)動(dòng) Java 代碼或應(yīng)用程序的引擎。它將 Java 字節(jié)碼轉(zhuǎn)換為機(jī)器語(yǔ)言搬泥。JVM 是 Java 運(yùn)行環(huán)境 (JRE) 的一部分桑寨。在其他編程語(yǔ)言中,編譯器為特定系統(tǒng)生成機(jī)器代碼忿檩。但是尉尾,Java編譯器為稱(chēng)為Java 虛擬機(jī)的虛擬機(jī)生成代碼。
JRE/JDK/JVM是什么關(guān)系
- JRE(
JavaRuntimeEnvironment
燥透,Java運(yùn)行環(huán)境)也就是Java平臺(tái)沙咏。所有的Java 程序都要在JRE
下才能運(yùn)行。普通用戶(hù)只需要運(yùn)行已開(kāi)發(fā)好的java程序班套,安裝JRE
即可肢藐。 - JDK(
Java Development Kit
)是程序開(kāi)發(fā)者用來(lái)來(lái)編譯、調(diào)試java程序用的開(kāi)發(fā)工具包吱韭。JDK
的工具也是Java程序吆豹,也需要JRE才能運(yùn)行。為了保持JDK的獨(dú)立性和完整性,在JDK的安裝過(guò)程中痘煤,JRE也是 安裝的一部分凑阶。所以在JDK的安裝目錄下有一個(gè)名為jre
的目錄,用于存放JRE
文件速勇。 - JVM(
JavaVirtualMachine晌砾,Java虛擬機(jī)
)是JRE的一部分。它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī)烦磁,是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的养匈。JVM有自己完善的硬件架構(gòu),如處理器都伪、堆棧呕乎、寄存器等,還具有相應(yīng)的指令系統(tǒng)陨晶。Java語(yǔ)言最重要的特點(diǎn)就是跨平臺(tái)運(yùn)行猬仁。使用JVM就是為了支持與操作系統(tǒng)無(wú)關(guān),實(shí)現(xiàn)跨平臺(tái)先誉。
JVM特點(diǎn)
- 跨平臺(tái)(移植性高)
Java能夠跨平臺(tái)的重要原因就是得益于JVM湿刽。
- 自動(dòng)內(nèi)存管理
JVM的工作原理
首先,Java 代碼被編譯成字節(jié)碼(javac編譯
)褐耳,這個(gè)字節(jié)碼在不同的機(jī)器上被解釋?zhuān)谥鳈C(jī)系統(tǒng)和 Java 源代碼之間诈闺,字節(jié)碼是一種中介語(yǔ)言,Java
中的JVM
負(fù)責(zé)分配內(nèi)存空間
JVM結(jié)構(gòu)
類(lèi)裝載器(
ClassLoader
)(用來(lái)裝.class
文件)執(zhí)行引擎(執(zhí)行字節(jié)碼铃芦,或者執(zhí)行本地方法)
運(yùn)行時(shí)數(shù)據(jù)區(qū)(方法區(qū)雅镊、堆、java棧又稱(chēng)為Java 虛擬機(jī)棧刃滓、PC寄存器又稱(chēng)為程序計(jì)數(shù)器仁烹、本地方法棧)
程序計(jì)數(shù)器(PC 寄存器)
-
程序計(jì)數(shù)器的定義
- 程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,是當(dāng)前線程正在執(zhí)行的那條字節(jié)碼指令的地址咧虎。若當(dāng)前線程正在執(zhí)行的是一個(gè)本地方法卓缰,那么此時(shí)程序計(jì)數(shù)器為
Undefined
。
- 程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,是當(dāng)前線程正在執(zhí)行的那條字節(jié)碼指令的地址咧虎。若當(dāng)前線程正在執(zhí)行的是一個(gè)本地方法卓缰,那么此時(shí)程序計(jì)數(shù)器為
-
程序計(jì)數(shù)器的作用
- 字節(jié)碼解釋器通過(guò)改變程序計(jì)數(shù)器來(lái)依次讀取指令砰诵,從而實(shí)現(xiàn)代碼的流程控制僚饭。
- 在多線程情況下,程序計(jì)數(shù)器記錄的是當(dāng)前線程執(zhí)行的位置胧砰,從而當(dāng)線程切換回來(lái)時(shí)鳍鸵,就知道上次線程執(zhí)行到哪了。
-
程序計(jì)數(shù)器的特點(diǎn)
- 是一塊較小的內(nèi)存空間尉间。
- 線程私有偿乖,每條線程都有自己的程序計(jì)數(shù)器击罪。
- 生命周期:隨著線程的創(chuàng)建而創(chuàng)建,隨著線程的結(jié)束而銷(xiāo)毀贪薪。
- 是唯一一個(gè)不會(huì)出現(xiàn)
OutOfMemoryError
的內(nèi)存區(qū)域媳禁。
Java 虛擬機(jī)棧(Java 棧)
-
Java 虛擬機(jī)棧的定義
- Java 虛擬機(jī)棧是描述 Java 方法運(yùn)行過(guò)程的內(nèi)存模型。
-
Java 虛擬機(jī)棧會(huì)為每一個(gè)即將運(yùn)行的 Java 方法創(chuàng)建一塊叫做“
棧幀
”的區(qū)域画切,用于存放該方法運(yùn)行過(guò)程中的一些信息竣稽,如:- 局部變量表
- 操作數(shù)棧
- 動(dòng)態(tài)鏈接
- 方法出口信息
-
......
Java棧.jpg
壓棧出棧過(guò)程
當(dāng)方法運(yùn)行過(guò)程中需要?jiǎng)?chuàng)建局部變量時(shí),就將局部變量的值存入棧幀中的局部變量表中霍弹。
Java 虛擬機(jī)棧的棧頂?shù)臈钱?dāng)前正在執(zhí)行的活動(dòng)棧毫别,也就是當(dāng)前正在執(zhí)行的方法,PC 寄存器也會(huì)指向這個(gè)地址典格。只有這個(gè)活動(dòng)的棧幀的本地變量可以被操作數(shù)棧使用岛宦,當(dāng)在這個(gè)棧幀中調(diào)用另一個(gè)方法,與之對(duì)應(yīng)的棧幀又會(huì)被創(chuàng)建耍缴,新創(chuàng)建的棧幀壓入棧頂砾肺,變?yōu)楫?dāng)前的活動(dòng)棧幀。
方法結(jié)束后防嗡,當(dāng)前棧幀被移出变汪,棧幀的返回值變成新的活動(dòng)棧幀中操作數(shù)棧的一個(gè)操作數(shù)。如果沒(méi)有返回值蚁趁,那么新的活動(dòng)棧幀中操作數(shù)棧的操作數(shù)沒(méi)有變化疫衩。
由于 Java 虛擬機(jī)棧是與線程對(duì)應(yīng)的,數(shù)據(jù)不是線程共享的荣德,因此不用關(guān)心數(shù)據(jù)一致性問(wèn)題,也不會(huì)存在同步鎖的問(wèn)題童芹。
-
Java 虛擬機(jī)棧的特點(diǎn)
- 局部變量表隨著棧幀的創(chuàng)建而創(chuàng)建涮瞻,它的大小在編譯時(shí)確定,創(chuàng)建時(shí)只需分配事先規(guī)定的大小即可假褪。在方法運(yùn)行過(guò)程中署咽,局部變量表的大小不會(huì)發(fā)生改變。
- Java 虛擬機(jī)棧會(huì)出現(xiàn)兩種異常:
StackOverFlowError
和OutOfMemoryError
生音。 -
StackOverFlowError
若 Java 虛擬機(jī)棧的大小不允許動(dòng)態(tài)擴(kuò)展宁否,那么當(dāng)線程請(qǐng)求棧的深度超過(guò)當(dāng)前 Java 虛擬機(jī)棧的最大深度時(shí),拋出StackOverFlowError
異常缀遍。 -
OutOfMemoryError
若允許動(dòng)態(tài)擴(kuò)展慕匠,那么當(dāng)線程請(qǐng)求棧時(shí)內(nèi)存用完了,無(wú)法再動(dòng)態(tài)擴(kuò)展時(shí)域醇,拋出OutOfMemoryError
異常台谊。 - Java 虛擬機(jī)棧也是線程私有蓉媳,隨著線程創(chuàng)建而創(chuàng)建,隨著線程的結(jié)束而銷(xiāo)毀锅铅。
出現(xiàn)
StackOverFlowError
時(shí)酪呻,內(nèi)存空間可能還有很多。
本地方法棧(C 棧)
- 本地方法棧的定義
本地方法棧是為 JVM 運(yùn)行Native 方法
準(zhǔn)備的空間盐须,由于很多Native 方法
都是用 C 語(yǔ)言
實(shí)現(xiàn)的玩荠,所以它通常又叫 C 棧
。它與 Java 虛擬機(jī)棧實(shí)現(xiàn)的功能類(lèi)似贼邓,只不過(guò)本地方法棧是描述本地方法運(yùn)行過(guò)程的內(nèi)存模型阶冈。
- 棧幀變化過(guò)程
本地方法被執(zhí)行時(shí),在本地方法棧也會(huì)創(chuàng)建一塊棧幀立帖,用于存放該方法的局部變量表眼溶、操作數(shù)棧、動(dòng)態(tài)鏈接晓勇、方法出口信息等堂飞。
方法執(zhí)行結(jié)束后,相應(yīng)的棧幀也會(huì)出棧绑咱,并釋放內(nèi)存空間绰筛。也會(huì)拋出 StackOverFlowError
和 OutOfMemoryError
異常。
如果 Java 虛擬機(jī)本身不支持 Native 方法描融,或是本身不依賴(lài)于傳統(tǒng)棧铝噩,那么可以不提供本地方法棧。如果支持本地方法棧窿克,那么這個(gè)棧一般會(huì)在線程創(chuàng)建的時(shí)候按線程分配骏庸。
堆
- 堆的定義
堆是用來(lái)存放對(duì)象的內(nèi)存空間,幾乎所有的對(duì)象都存儲(chǔ)在堆中年叮。
-
堆的特點(diǎn)
- 線程共享具被,整個(gè) Java 虛擬機(jī)只有一個(gè)堆,所有的線程都訪問(wèn)同一個(gè)堆只损。而程序計(jì)數(shù)器一姿、Java 虛擬機(jī)棧、本地方法棧都是一個(gè)線程對(duì)應(yīng)一個(gè)跃惫。
- 在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建叮叹。
- 是垃圾回收的主要場(chǎng)所。
- 進(jìn)一步可分為:新生代(Eden 區(qū):
From Survior
爆存,To Survivor
)蛉顽、老年代。
不同的區(qū)域存放不同生命周期的對(duì)象先较,這樣可以根據(jù)不同的區(qū)域使用不同的垃圾回收算法蜂林,更具有針對(duì)性遥诉。
堆的大小既可以固定也可以擴(kuò)展,但對(duì)于主流的虛擬機(jī)噪叙,堆的大小是可擴(kuò)展的矮锈,因此當(dāng)線程請(qǐng)求分配內(nèi)存,但堆已滿睁蕾,且內(nèi)存已無(wú)法再擴(kuò)展時(shí)苞笨,就拋出 OutOfMemoryError 異常。
Java 堆所使用的內(nèi)存不需要保證是連續(xù)的子眶。而由于堆是被所有線程共享的瀑凝,所以對(duì)它的訪問(wèn)需要注意同步問(wèn)題,方法和對(duì)應(yīng)的屬性都需要保證一致性臭杰。
方法區(qū)
-
方法區(qū)的定義
-
Java 虛擬機(jī)規(guī)范中定義方法區(qū)是堆的一個(gè)邏輯部分粤咪。方法區(qū)存放以下信息:
- 已經(jīng)被虛擬機(jī)加載的類(lèi)信息
- 常量
- 靜態(tài)變量
- 即時(shí)編譯器編譯后的代碼
-
-
方法區(qū)的特點(diǎn)
- 線程共享。 方法區(qū)是堆的一個(gè)邏輯部分渴杆,因此和堆一樣寥枝,都是線程共享的。整個(gè)虛擬機(jī)中只有一個(gè)方法區(qū)磁奖。
- 永久代囊拜。 方法區(qū)中的信息一般需要長(zhǎng)期存在,而且它又是堆的邏輯分區(qū)比搭,因此用堆的劃分方法冠跷,把方法區(qū)稱(chēng)為“永久代”。
- 內(nèi)存回收效率低身诺。 方法區(qū)中的信息一般需要長(zhǎng)期存在蜜托,回收一遍之后可能只有少量信息無(wú)效。主要回收目標(biāo)是:對(duì)常量池的回收霉赡;對(duì)類(lèi)型的卸載橄务。
- Java 虛擬機(jī)規(guī)范對(duì)方法區(qū)的要求比較寬松。 和堆一樣同廉,允許固定大小,也允許動(dòng)態(tài)擴(kuò)展柑司,還允許不實(shí)現(xiàn)垃圾回收迫肖。
運(yùn)行時(shí)常量池
方法區(qū)中存放:類(lèi)信息、常量攒驰、靜態(tài)變量蟆湖、即時(shí)編譯器編譯后的代碼。常量就存放在運(yùn)行時(shí)常量池中玻粪。
當(dāng)類(lèi)被 Java 虛擬機(jī)加載后隅津,.class
文件中的常量就存放在方法區(qū)的運(yùn)行時(shí)常量池中诬垂。而且在運(yùn)行期間,可以向常量池中添加新的常量伦仍。如 String 類(lèi)的 intern()
方法就能在運(yùn)行期間向常量池中添加字符串常量结窘。