JVM運(yùn)行原理詳解

1.JVM簡(jiǎn)析:

?????作為一名Java使用者,掌握J(rèn)VM的體系結(jié)構(gòu)也是很有必要的。

?????說(shuō)起Java,我們首先想到的是Java編程語(yǔ)言,然而事實(shí)上,Java是一種技術(shù),它由四方面組成:Java編程語(yǔ)言、Java類(lèi)文件格式、Java虛擬機(jī)和Java應(yīng)用程序接口(Java API)。它們的關(guān)系如下圖所示:

? ? ?Java平臺(tái)由Java虛擬機(jī)和Java應(yīng)用程序接口搭建,Java語(yǔ)言則是進(jìn)入這個(gè)平臺(tái)的通道,用Java語(yǔ)言編寫(xiě)并編譯的程序可以運(yùn)行在這個(gè)平臺(tái)上。這個(gè)平臺(tái)的結(jié)構(gòu)如下圖所示:? ? ?運(yùn)行期環(huán)境代表著Java平臺(tái),開(kāi)發(fā)人員編寫(xiě)Java代碼(.java文件),然后將之編譯成字節(jié)碼(.class文件),再然后字節(jié)碼被裝入內(nèi)存,一旦字節(jié)碼進(jìn)入虛擬機(jī),它就會(huì)被解釋器解釋執(zhí)行,或者是被即時(shí)代碼發(fā)生器有選擇的轉(zhuǎn)換成機(jī)器碼執(zhí)行。

? ? ?JVM在它的生存周期中有一個(gè)明確的任務(wù),那就是運(yùn)行Java程序,因此當(dāng)Java程序啟動(dòng)的時(shí)候,就產(chǎn)生JVM的一個(gè)實(shí)例;當(dāng)程序運(yùn)行結(jié)束的時(shí)候,該實(shí)例也跟著消失了。?????在Java平臺(tái)的結(jié)構(gòu)中, 可以看出,Java虛擬機(jī)(JVM) 處在核心的位置,是程序與底層操作系統(tǒng)和硬件無(wú)關(guān)的關(guān)鍵。它的下方是移植接口,移植接口由兩部分組成:適配器和Java操作系統(tǒng), 其中依賴(lài)于平臺(tái)的部分稱(chēng)為適配器;JVM 通過(guò)移植接口在具體的平臺(tái)和操作系統(tǒng)上實(shí)現(xiàn);在JVM 的上方是Java的基本類(lèi)庫(kù)和擴(kuò)展類(lèi)庫(kù)以及它們的API, 利用Java API編寫(xiě)的應(yīng)用程序(application) 和小程序(Java applet) 可以在任何Java平臺(tái)上運(yùn)行而無(wú)需考慮底層平臺(tái), 就是因?yàn)橛蠮ava虛擬機(jī)(JVM)實(shí)現(xiàn)了程序與操作系統(tǒng)的分離,從而實(shí)現(xiàn)了Java 的平臺(tái)無(wú)關(guān)性。

?????下面我們從JVM的基本概念和運(yùn)過(guò)程程這兩個(gè)方面入手來(lái)對(duì)它進(jìn)行深入的研究。

2.JVM基本概念

(1) 基本概念:

?????JVM是可運(yùn)行Java代碼的假想計(jì)算機(jī) ,包括一套字節(jié)碼指令集、一組寄存器、一個(gè)棧、一個(gè)垃圾回收,堆 和 一個(gè)存儲(chǔ)方法域。JVM是運(yùn)行在操作系統(tǒng)之上的,它與硬件沒(méi)有直接的交互。

(2) 運(yùn)行過(guò)程:

?????我們都知道Java源文件,通過(guò)編譯器,能夠生產(chǎn)相應(yīng)的.Class文件,也就是字節(jié)碼文件,而字節(jié)碼文件又通過(guò)Java虛擬機(jī)中的解釋器,編譯成特定機(jī)器上的機(jī)器碼 。

也就是如下:

? ?? ① Java源文件—->編譯器—->字節(jié)碼文件

? ?? ② 字節(jié)碼文件—->JVM—->機(jī)器碼

?????每一種平臺(tái)的解釋器是不同的,但是實(shí)現(xiàn)的虛擬機(jī)是相同的,這也就是Java為什么能夠跨平臺(tái)的原因了 ,當(dāng)一個(gè)程序從開(kāi)始運(yùn)行,這時(shí)虛擬機(jī)就開(kāi)始實(shí)例化了,多個(gè)程序啟動(dòng)就會(huì)存在多個(gè)虛擬機(jī)實(shí)例。程序退出或者關(guān)閉,則虛擬機(jī)實(shí)例消亡,多個(gè)虛擬機(jī)實(shí)例之間數(shù)據(jù)不能共享。

(3) 三種JVM:

? ? ?① Sun公司的HotSpot;

? ? ?② BEA公司的JRockit;

? ? ?③ IBM公司的J9 JVM;

?????在JDK1.7及其以前我們所使用的都是Sun公司的HotSpot,但由于Sun公司和BEA公司都被oracle收購(gòu),jdk1.8將采用Sun公司的HotSpot和BEA公司的JRockit兩個(gè)JVM中精華形成jdk1.8的JVM。

3.JVM的體系結(jié)構(gòu)

(1) Class Loader類(lèi)加載器

? ? ???負(fù)責(zé)加載 .class文件,class文件在文件開(kāi)頭有特定的文件標(biāo)示,并且ClassLoader負(fù)責(zé)class文件的加載等,至于它是否可以運(yùn)行,則由Execution Engine決定。

① 定位和導(dǎo)入二進(jìn)制class文件

② 驗(yàn)證導(dǎo)入類(lèi)的正確性

③ 為類(lèi)分配初始化內(nèi)存

④ 幫助解析符號(hào)引用.

(2) Native Interface本地接口:

本地接口的作用是融合不同的編程語(yǔ)言為Java所用,它的初衷是融合C/C++程序,Java誕生的時(shí)候C/C++橫行的時(shí)候,要想立足,必須有調(diào)用C/C++程序,于是就在內(nèi)存中專(zhuān)門(mén)開(kāi)辟了一塊區(qū)域處理標(biāo)記為native的代碼,它的具體作法是Native Method Stack中登記native方法,在Execution Engine執(zhí)行時(shí)加載native libraies。

目前該方法使用的越來(lái)越少了,除非是與硬件有關(guān)的應(yīng)用,比如通過(guò)Java程序驅(qū)動(dòng)打印機(jī),或者Java系統(tǒng)管理生產(chǎn)設(shè)備,在企業(yè)級(jí)應(yīng)用中已經(jīng)比較少見(jiàn)。

因?yàn)楝F(xiàn)在的異構(gòu)領(lǐng)域間的通信很發(fā)達(dá),比如可以使用Socket通信,也可以使用Web Service等。

(3) Execution Engine 執(zhí)行引擎:執(zhí)行包在裝載類(lèi)的方法中的指令,也就是方法。

(4) Runtime data area 運(yùn)行數(shù)據(jù)區(qū):

? ? ??虛擬機(jī)內(nèi)存或者Jvm內(nèi)存,沖整個(gè)計(jì)算機(jī)內(nèi)存中開(kāi)辟一塊內(nèi)存存儲(chǔ)Jvm需要用到的對(duì)象,變量等,運(yùn)行區(qū)數(shù)據(jù)有分很多小區(qū),分別為:方法區(qū),虛擬機(jī)棧,本地方法棧,堆,程序計(jì)數(shù)器。

4.JVM數(shù)據(jù)運(yùn)行區(qū)詳解(棧管運(yùn)行,堆管存儲(chǔ)):

? ? ?說(shuō)明:JVM調(diào)優(yōu)主要就是優(yōu)化?Heap堆 和?Method Area 方法區(qū)。

(1) Native Method Stack本地方法棧

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

(2) PC Register程序計(jì)數(shù)器

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

(3) Method Area方法區(qū)

???????? 方法區(qū)是被所有線程共享,所有字段和方法字節(jié)碼,以及一些特殊方法如構(gòu)造函數(shù),接口代碼也在此定義。簡(jiǎn)單說(shuō),所有定義的方法的信息都保存在該區(qū)域,此區(qū)域?qū)儆诠蚕韰^(qū)間。

? ? ? ? ?靜態(tài)變量+常量+類(lèi)信息+運(yùn)行時(shí)常量池存在方法區(qū)中,實(shí)例變量存在堆內(nèi)存中。

(4) Stack 棧

? ?? ① 棧是什么

???????? 棧也叫棧內(nèi)存,主管Java程序的運(yùn)行,是在線程創(chuàng)建時(shí)創(chuàng)建,它的生命期是跟隨線程的生命期,線程結(jié)束棧內(nèi)存也就釋放,對(duì)于棧來(lái)說(shuō)不存在垃圾回收問(wèn)題,只要線程一結(jié)束該棧就Over,生命周期和線程一致,是線程私有的。

? ?? ?????基本類(lèi)型的變量和對(duì)象的引用變量都是在函數(shù)的棧內(nèi)存中分配。

? ?? ② 棧存儲(chǔ)什么?

?????棧幀中主要保存3類(lèi)數(shù)據(jù):

? ?? ?????本地變量(Local Variables):輸入?yún)?shù)和輸出參數(shù)以及方法內(nèi)的變量;

? ?? ?????棧操作(Operand Stack):記錄出棧、入棧的操作;

? ?? ?????棧幀數(shù)據(jù)(Frame Data):包括類(lèi)文件、方法等等。

? ?? ③?棧運(yùn)行原理

?????棧中的數(shù)據(jù)都是以棧幀(Stack Frame)的格式存在,棧幀是一個(gè)內(nèi)存區(qū)塊,是一個(gè)數(shù)據(jù)集,是一個(gè)有關(guān)方法和運(yùn)行期數(shù)據(jù)的數(shù)據(jù)集,當(dāng)一個(gè)方法A被調(diào)用時(shí)就產(chǎn)生了一個(gè)棧幀F(xiàn)1,并被壓入到棧中,A方法又調(diào)用了B方法,于是產(chǎn)生棧幀F(xiàn)2也被壓入棧,B方法又調(diào)用了C方法,于是產(chǎn)生棧幀F(xiàn)3也被壓入棧…… 依次執(zhí)行完畢后,先彈出后進(jìn)……F3棧幀,再?gòu)棾鯢2棧幀,再?gòu)棾鯢1棧幀。

?????遵循“先進(jìn)后出”/“后進(jìn)先出”原則。

(5) Heap 堆

?????堆這塊區(qū)域是JVM中最大的,應(yīng)用的對(duì)象和數(shù)據(jù)都是存在這個(gè)區(qū)域,這塊區(qū)域也是線程共享的,也是 gc 主要的回收區(qū),一個(gè) JVM 實(shí)例只存在一個(gè)堆類(lèi)存,堆內(nèi)存的大小是可以調(diào)節(jié)的。類(lèi)加載器讀取了類(lèi)文件后,需要把類(lèi)、方法、常變量放到堆內(nèi)存中,以方便執(zhí)行器執(zhí)行,堆內(nèi)存分為三部分:

??? ? ?①?新生區(qū)

? ? ?? 新生區(qū)是類(lèi)的誕生、成長(zhǎng)、消亡的區(qū)域,一個(gè)類(lèi)在這里產(chǎn)生,應(yīng)用,最后被垃圾回收器收集,結(jié)束生命。新生區(qū)又分為兩部分:伊甸區(qū)(Eden space)和幸存者區(qū)(Survivor pace),所有的類(lèi)都是在伊甸區(qū)被new出來(lái)的。幸存區(qū)有兩個(gè):0區(qū)(Survivor 0 space)和1區(qū)(Survivor 1 space)。當(dāng)伊甸園的空間用完時(shí),程序又需要?jiǎng)?chuàng)建對(duì)象,JVM的垃圾回收器將對(duì)伊甸園進(jìn)行垃圾回收(Minor GC),將伊甸園中的剩余對(duì)象移動(dòng)到幸存0區(qū)。若幸存0區(qū)也滿了,再對(duì)該區(qū)進(jìn)行垃圾回收,然后移動(dòng)到1區(qū)。那如果1去也滿了呢?再移動(dòng)到養(yǎng)老區(qū)。若養(yǎng)老區(qū)也滿了,那么這個(gè)時(shí)候?qū)a(chǎn)生Major GC(FullGCC),進(jìn)行養(yǎng)老區(qū)的內(nèi)存清理。若養(yǎng)老區(qū)執(zhí)行Full GC 之后發(fā)現(xiàn)依然無(wú)法進(jìn)行對(duì)象的保存,就會(huì)產(chǎn)生OOM異?!癘utOfMemoryError”。

?????如果出現(xiàn)java.lang.OutOfMemoryError: Java heap space異常,說(shuō)明Java虛擬機(jī)的堆內(nèi)存不夠。原因有二:

? ??a.Java虛擬機(jī)的堆內(nèi)存設(shè)置不夠,可以通過(guò)參數(shù)-Xms、-Xmx來(lái)調(diào)整。

? ???b.代碼中創(chuàng)建了大量大對(duì)象,并且長(zhǎng)時(shí)間不能被垃圾收集器收集(存在被引用)。

? ? ?②?養(yǎng)老區(qū)

? ? ? ?? 養(yǎng)老區(qū)用于保存從新生區(qū)篩選出來(lái)的 JAVA 對(duì)象,一般池對(duì)象都在這個(gè)區(qū)域活躍。

? ? ?③?永久區(qū)

? ? ? ?? 永久存儲(chǔ)區(qū)是一個(gè)常駐內(nèi)存區(qū)域,用于存放JDK自身所攜帶的 Class,Interface 的元數(shù)據(jù),也就是說(shuō)它存儲(chǔ)的是運(yùn)行環(huán)境必須的類(lèi)信息,被裝載進(jìn)此區(qū)域的數(shù)據(jù)是不會(huì)被垃圾回收器回收掉的,關(guān)閉 JVM 才會(huì)釋放此區(qū)域所占用的內(nèi)存。

? ???如果出現(xiàn)java.lang.OutOfMemoryError: PermGen space,說(shuō)明是Java虛擬機(jī)對(duì)永久代Perm內(nèi)存設(shè)置不夠。原因有二:

? ???a.?程序啟動(dòng)需要加載大量的第三方j(luò)ar包。例如:在一個(gè)Tomcat下部署了太多的應(yīng)用。

? ? ?b. 大量動(dòng)態(tài)反射生成的類(lèi)不斷被加載,最終導(dǎo)致Perm區(qū)被占滿。

? ???說(shuō)明:

?????Jdk1.6及之前:常量池分配在永久代 。

?????Jdk1.7:有,但已經(jīng)逐步“去永久代” 。

?????Jdk1.8及之后:無(wú)(java.lang.OutOfMemoryError: PermGen space,這種錯(cuò)誤將不會(huì)出現(xiàn)在JDK1.8中)。

? ? ?說(shuō)明:方法區(qū)和堆內(nèi)存的異議:

?????實(shí)際而言,方法區(qū)和堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)虛擬機(jī)加載的:類(lèi)信息+普通常量+靜態(tài)常量+編譯器編譯后的代碼等等,雖然JVM規(guī)范將方法區(qū)描述為堆的一個(gè)邏輯部分,但它卻還有一個(gè)別名叫做Non-Heap(非堆),目的就是要和堆分開(kāi)。

? ?? 對(duì)于HotSpot虛擬機(jī),很多開(kāi)發(fā)者習(xí)慣將方法區(qū)稱(chēng)之為“永久代(Parmanent Gen)”,但嚴(yán)格本質(zhì)上說(shuō)兩者不同,或者說(shuō)使用永久代來(lái)實(shí)現(xiàn)方法區(qū)而已,永久代是方法區(qū)的一個(gè)實(shí)現(xiàn),jdk1.7的版本中,已經(jīng)將原本放在永久代的字符串常量池移走。

? ?? 常量池(Constant Pool)是方法區(qū)的一部分,Class文件除了有類(lèi)的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是常量池,這部分內(nèi)容將在類(lèi)加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。

在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流群。交流學(xué)習(xí)群號(hào):575745314 里面會(huì)分享一些資深架構(gòu)師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識(shí)體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多


5.堆內(nèi)存調(diào)優(yōu)簡(jiǎn)介

代碼測(cè)試:

說(shuō)明:在Run as ->Run Configurations中輸入”-XX:+PrintGCDetails”可以查看堆內(nèi)存運(yùn)行原理圖:

(1)?在jdk1.7中:

?(2) 在jdk1.8中:

6.通過(guò)參數(shù)設(shè)置自動(dòng)觸發(fā)垃圾回收:

在Run as ->Run Configurations中輸入設(shè)置“-Xmx8m –Xms8m –xx:+PrintGCDetails”可以參看垃圾回收機(jī)制原理:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • jvm原理 Java虛擬機(jī)是整個(gè)java平臺(tái)的基石,是java技術(shù)實(shí)現(xiàn)硬件無(wú)關(guān)和操作系統(tǒng)無(wú)關(guān)的關(guān)鍵環(huán)節(jié),是java...
    AI喬治閱讀 17,539評(píng)論 21 486
  • 內(nèi)存溢出和內(nèi)存泄漏的區(qū)別 內(nèi)存溢出:out of memory,是指程序在申請(qǐng)內(nèi)存時(shí),沒(méi)有足夠的內(nèi)存空間供其使用,...
    Aimerwhy閱讀 799評(píng)論 0 1
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,761評(píng)論 11 349
  • 最近有朋友在討論這個(gè)親密關(guān)系,話說(shuō)我是最不會(huì)處理親密關(guān)系的一個(gè)人,我經(jīng)常說(shuō)的話是我只撩女的不撩男的,問(wèn)其原因應(yīng)該是...
    歲月靜好_6a15閱讀 203評(píng)論 0 0
  • 同學(xué)離開(kāi)十幾天了,翻看同學(xué)群里最近的消息還是關(guān)于同學(xué)離世的信息,悲傷的情緒彌漫期間,還是會(huì)有淚涔然。 那...
    我自萱華閱讀 376評(píng)論 0 0

友情鏈接更多精彩內(nèi)容