被阿里p7級別面試官侮辱后,我決定通宵研究JVM!

一: 前言

  • 面試官: 嗯、談?wù)勀銓VM、JRE、JDK的認(rèn)識吧。

  • 我: (心里想)小樣,就這問題,想難倒資深CURD工程師,還好我早有準(zhǔn)備?;卮鸬?JVM全稱JAVA虛擬機,它可用于加載JAVA字節(jié)碼文件,可以看做是JAVA的一個執(zhí)行環(huán)境,JAVA跨平臺特性也是因為有JVM。

  • 面試官: 說完了嗎、就這?

  • 我: 一聽面試官這語氣,心里直冒火,錢是小事,面子可不能丟,敢看不起一個多年經(jīng)驗的CURD工程師,這面試不給你找回來,剛要繼續(xù)回答!

  • 面試官: 擺了擺手,有氣無力道,好吧,今天先這樣,后面有消息的話人事會通知你的。

  • 我: 一聽面試官這語氣,我更生氣了,便說道,三十年河?xùn)|、三十年河西、莫欺少年窮!說完就站了起來,準(zhǔn)備走出面試室。

  • 面試官: 臉一抽,嘲笑說道,你以為你是蕭炎?我TM還是蕭站呢!

  • 我: 我一聽他這回答、這語氣!擺明了是占我便宜啊,體內(nèi)的洪荒之力直接控制不住,掄起桌面的水瓶就往面試官臉上招呼過去!

  • 我: 正在我打的正爽,正在面試官連連向我求饒的時候,腳一抽,我突然醒了,發(fā)現(xiàn)自己躺在床上,時間是凌晨2點。原來是做夢啊!早知道就打狠一點了,我心里暗暗說道!

  • 我: 此刻的我一點睡意都沒有了,回想起在夢中看到的面試官嘴臉,我在心里暗暗發(fā)誓,我絕對不允許夢里的場景在現(xiàn)實重現(xiàn),在面試官裝B之前,我就要比他先裝!

  • 我: 從床上爬了起來,打開電腦,在界面輸入: JVM、JRE、JDK有什么區(qū)別!

二: 什么是JVM

那接下來我將重點聊聊,希望給你以借鑒!大家看完覺得還不錯的話,別忘了點個贊哦!碼字不易

戳我GitHub主頁學(xué)習(xí)更多技巧優(yōu)化

什么是JVM

(一): JVM

在認(rèn)識JVM是什么之前,先回想一下平日里,如果我們想要在電腦上使用一個應(yīng)用,應(yīng)該具備什么條件?

首先,要有個環(huán)境(電腦就是),其次需要將這個應(yīng)用安裝到電腦上,然后,我們才能夠使用應(yīng)用來做我們想做的事情。

JVM也是類似,JVM全稱Java Virtul Machine(JAVA虛擬機),它是一種計算設(shè)備的規(guī)范,可以理解成是一個虛構(gòu)的計算機,它里面包含了和真實計算機相似的組件,用于模仿計算機的各種功能,然后運行在真實的計算機中

(二): JAVA為什么說是“平臺無關(guān)的編程語言”

因為它包含了JAVA虛擬機(JVM),JVM知道底層硬件平臺的指令長度和其他相關(guān)特性,它屏蔽了和具體平臺的相關(guān)信息,使得JAVA語言開發(fā)的程序只需要一次編譯生成JVM上能夠運行的目標(biāo)代碼(字節(jié)碼文件),從而可以在不同平臺上運行時而不需要進行重新編譯

(三): JVM內(nèi)部結(jié)構(gòu)

注:通常情況下我們使用的JDK是由Sun JDK和OpenJDK提供的,這個是應(yīng)用最廣泛的版本。而這個版本的虛擬機就是Hotspot(熱點數(shù)據(jù)),所以通常情況下,我們講的JAVA虛擬機默認(rèn)就是指Hotspot。

JVM的內(nèi)部可以具體區(qū)分為三大部分即: 類加載器、執(zhí)行引擎、運行時數(shù)據(jù)區(qū)。

JVM內(nèi)部結(jié)構(gòu)

一:類加載器(Class Loader)

它的作用主要是將類的字節(jié)碼文件(class文件)從磁盤加載到內(nèi)存、然后對class文件進行數(shù)據(jù)校驗、轉(zhuǎn)換解析、初始化等操作,最終轉(zhuǎn)成可以被虛擬機直接使用的Class對象。

二:執(zhí)行引擎(Execution Engine)

JVM中的最核心組件之一,主要用于執(zhí)行指令,用于將加載到JVM中的字節(jié)碼文件解釋或者編譯成對應(yīng)平臺上的本地機器指令,簡單來說,它充當(dāng)著將高級語言翻譯成機器語言的中間者

并且不同的JVM內(nèi)部實現(xiàn)也不一樣,執(zhí)行引擎在執(zhí)行JAVA代碼的時候可能是解釋執(zhí)行(解釋器執(zhí)行)和編譯執(zhí)行(即時編譯器產(chǎn)生本地代碼執(zhí)行),也可能兩者都有。

三:什么是解釋器(Interpreter),什么是JIT編譯器?

(一) 解釋器:

根據(jù)預(yù)定義的規(guī)范負(fù)責(zé)將加載到JVM中的字節(jié)碼進行逐行解釋的方式執(zhí)行,將每條字節(jié)碼翻譯成對應(yīng)平臺的本地機器指令執(zhí)行,每調(diào)用一次就需要編譯一次。

(二) JIT(Just In Time Compiler)即時編譯器: 它是一種提高程序運行效率的方法,它負(fù)責(zé)將虛擬機中的源代碼直接編譯成和本地機器平臺相關(guān)的機器語言,然后將熱點代碼的機器碼保存起來,后面再調(diào)用時直接執(zhí)行這些機器碼即可。

Hotspot(熱點數(shù)據(jù))虛擬機命名的由來,當(dāng)虛擬機發(fā)現(xiàn)某個方法或者代碼塊運行的特別頻繁時,就會將這些代碼認(rèn)定為"熱點代碼",為了提高熱點代碼的執(zhí)行效率,在運行時,虛擬機會將這些代碼編譯成與本地平臺相關(guān)的機器碼,并進行各種層次的優(yōu)化,下次再調(diào)用時就可以直接執(zhí)行機器碼即可,JIT編譯器就是用來完成這個任務(wù)的。

(三)特點:

1、解釋器執(zhí)行步驟可以抽象理解為:

字節(jié)碼文件 -> 解釋器進行解釋執(zhí)行 ->得到執(zhí)行結(jié)果

2、JIT可以抽象理解為:

字節(jié)碼文件 -> 編譯器進行編譯 -> 執(zhí)行編譯后的代碼 -> 得到執(zhí)行結(jié)果

JIT比解釋器快,并不是指"編譯"的動作比"解釋"的動作快,而是指"執(zhí)行編譯后的代碼"會比"使用解釋器解釋執(zhí)行"的動作要更快。

如果是只執(zhí)行一次的代碼(只調(diào)用一次且沒有循環(huán))如:類的構(gòu)造器,使用解釋器的效率要比JIT編譯執(zhí)行更快,只有是頻繁執(zhí)行的熱點代碼,使用JIT方式才能夠有更好的效率

四:堆(Heap)

線程共享、JVM虛擬機啟動時創(chuàng)建、是占用JVM內(nèi)存最大的一塊區(qū)域,主要是用來存放對象,因為是線程共享,所以、存在此處的數(shù)據(jù)可能存在線程安全問題。

1、堆的內(nèi)部結(jié)構(gòu)劃分

新生代(Young)和老年代(Old),它們的大小比例是:1:2 兩部分。而新生代又劃分為三個區(qū)域: Eden區(qū)、From Survivor區(qū)、To Survivor區(qū)、它們的大小比例是:8:1:1,具體模型如下:

堆的內(nèi)部結(jié)構(gòu)

2、為什么要將堆劃分成這種結(jié)構(gòu)

給堆進行分代的目的是為了提高內(nèi)存使用率和垃圾回收的效率。 因為堆是占JVM中最大空間一塊區(qū)域,所有的對象實例都存在這個地方,同時它也是垃圾回收最頻繁的區(qū)域,如果沒有對它進行分代管理,新創(chuàng)建的對象和生命周期很長的對象統(tǒng)一管理,則當(dāng)程序需要進行垃圾回收的時候,每次回收都要對所有對象進行判斷,這個判斷是非?;ㄙM時間的,會嚴(yán)重影響GC的效率

有了分代管理機制,新創(chuàng)建的對象存在新生代,經(jīng)過多次回收仍然還存活的對象則放入老年代(默認(rèn)是15次),因為新生代的對象都是"朝生暮死",生命周期比較短,所以需要頻繁進行GC,而老年代生命周期長,回收的頻率會比新生代低,這樣就避免了每次回收的時候還要去判斷所有對象的狀態(tài),提高了GC的效率。

一個很簡單的例子,其實跟我們平常生活中的年輕人和老年人參加的活動相似,老年人比較樂忠于廣場舞、超市打折活動,年輕人比較樂忠于新科技、游戲活動,參加一個活動的時候,是有針對不同的用戶群體的,你不可能有廣場舞這種活動還一直拉著年輕人去參加吧,同理的,你也不可能覺老年人整天去玩游戲一個道理,分代管理可以針對不一樣的場景用不同的管理方式。

3、 新生代(Young Generation)

新生成的對象優(yōu)先存放在新生代中,新生代中的對象有著“朝生夕死”的特點,存活率很低。所以、在新生代中,每次進行GC回收都可以回收很大的空間、回收效率很高。

在上面的堆結(jié)構(gòu)圖中可以看到,Hotspot虛擬機將新生代劃分成了三塊,一塊占據(jù)最大空間的Eden(伊甸)區(qū)和兩塊占據(jù)較小空間的Survisor(幸存者)區(qū),默認(rèn)的比例是:8:1:1。

將新生代劃分為三區(qū)域的目的是因為在Hotspot中是采用復(fù)制算法來回收新生代的垃圾對象的(具體的垃圾算法會在下篇講解,感興趣的可以持續(xù)關(guān)注),使用這個默認(rèn)的比例可以更加充分的利用內(nèi)存空間,減少浪費,所以這個比例大小一般不要去變動。

特點: 新創(chuàng)建的對象默認(rèn)是分配在Eden區(qū)(但是大對象除外,大對象是直接存放入老年代區(qū)域),如果Eden區(qū)沒有足夠的空間分配給對象時,則JVM會觸發(fā)一起Minor GC來進行回收垃圾對象,釋放空間。

4、 新生代GC回收的流程:

首先,在GC前,新生代中To Survivor區(qū)是空的(用于保留存活對象),對象只存在Eden區(qū)和From Survivor區(qū)。

開始GC時,JVM會將GC中存活的對象都復(fù)制到To Survivor區(qū)中,From Survivor區(qū)中的對象會根據(jù)存活年齡有不同的去向,如果年齡達到了閾值(Hotspot JVM默認(rèn)是15,對象每經(jīng)過一輪垃圾回收存活則年齡值加1,并且這個值是存放有對象頭中)則會將這個對象放入老年代,如果沒有達到年齡閾值則將該對象復(fù)制到To Survivor區(qū)。

完成復(fù)制操作后,GC線程會清空Eden區(qū)和From Survivor區(qū),所有的存活對象都存放在To Survivor區(qū)。緊接著,From Survivor區(qū)和To Survivor區(qū)會交換指針,To Survivor則指向了From Survivor區(qū),From Survivor區(qū)指向了To Survivor區(qū)既一輪GC后,無論如何To Survivor都是空的,它只是作為GC中的一個中間者。如果在一次GC中,To Survivor的控件無法存放新生代中存活的所有對象時,需要進行分配擔(dān)保,將這個對象存放在老年代。

在這里插入圖片描述

5、 老年代(Old Generation)

存放在老年代的對象有以下的情況:

1、在新生代中經(jīng)歷了多次GC,年齡值達到了JVM中的閾值(Hotspot默認(rèn)是15)仍存活的對象

2、大對象既指需要比較大的連續(xù)控件的JAVA對象,如很長的數(shù)組對象和字符串對象,具體可以通過JVM指定-XX:PretenureSizeThreshold參數(shù)來設(shè)置大對象的大小,單位為字節(jié)(byte)

3、Minor GC后存活的對象占用空間的大小超過了To Survivor區(qū)的大小時,會將超出的對象進行老年代分配擔(dān)保放入老年代中。

4、如果在Survivor區(qū)中,某一個年齡的對象總大小超過了Survivor區(qū)大小的50%,則會將這個大于或者等于這個年齡的對象全部轉(zhuǎn)移到老年代。

5、在每次執(zhí)行Minor GC前,JVM會進行檢查老年代當(dāng)前剩余的空間是否大于年輕代所有對象的總大小,目的是為了防止年輕代的對象在GC后全部滿足進入老年代的條件,然后進行轉(zhuǎn)移將老年代撐爆。只要老年代的連續(xù)空間大于年輕代對象的總大小或者小于歷次晉升到老年代對象的年齡平均值則進行Minor GC否則進行Full GC。

6、 堆垃圾回收方式

1、Minor GC(YGC): 它主要是用來對新生代進行垃圾回收的方式,使用的復(fù)制算法,因為新生代的對象大多數(shù)都是"朝生暮死",生命周期很短的特點,所以GC的頻率也會比較頻繁,但是回收速度很快。

2、Major GC(YGC): 它是主要用于對老年代對象的垃圾回收方式,老年代的對象生命周期都是比較長的,所以對象不會輕易滅亡,Major GC的頻率不會想Minor GC那么頻繁,況且一次Full GC會比Minor GC需要花費更多的時間、消耗更大,通常出現(xiàn)一次Major GC一般也會出現(xiàn)一次Minor GC(但不絕對)。

3、Full GC(): Full GC是針對整個新生代、老生代、元空間(metaspace,java8以上版本取代perm gen)的全局范圍的GC,但是它并不等于Major GC + Minor GC,具體是要看使用什么垃圾收集器組合。一次Full GC 需要花費更多的時間、消耗更大,所以要盡減少Full GC的次數(shù)。

特點比較: Minor GC使用復(fù)制算法,需要一塊空的內(nèi)存空間,所以空間使用效率不高,但是它不會出現(xiàn)空間碎片的問題。而Full GC一般是采用標(biāo)記-清除算法,容易產(chǎn)生空間碎片,如果再有對象需要請求連續(xù)的空間而無法提供時,會提前觸發(fā)垃圾回收,所以它適合存活對象較多的場景使用也就是老年代的垃圾回收。

7、 堆垃圾回收算法

因為垃圾回收算法的細(xì)節(jié)比較多,所以專門使用一篇文章進行講解,感興趣的可以持續(xù)關(guān)注!

五:方法區(qū)(Method Area)

也稱為非堆(No-Heap)、是線程共享的一塊內(nèi)存區(qū)域、主要是存放類加載后的字節(jié)碼文件、class、method、field等元數(shù)據(jù)、靜態(tài)常量、變量以及JIT編譯器編譯的機器指令等數(shù)據(jù),除此之外,還包括“運行時常量池”。

1、方法區(qū)的實現(xiàn)(Method Area)

方法區(qū)是一個規(guī)范,可以理解成JAVA語言中的接口,它可以有不同的實現(xiàn)方式,常見的實現(xiàn)方式有: 永久代和元空間

永久代(Permanent Generation): 在JDK1.7及以前,它是方法區(qū)的實現(xiàn),但是永久代的概念只有Hotspot虛擬機有,其他的虛擬機如J9、JRockit虛擬機就沒有。

元空間(MetaSpace): Hotspot虛擬機(也就是我們平常使用的Oracle的虛擬機)在JDK1.8版本移除了永久代,使用元空間替代了它,元空間占用的是系統(tǒng)內(nèi)存,換個說法,只要系統(tǒng)的內(nèi)存空間還充足,方法區(qū)就會存在足夠的空間,但是,并不意味著我們不需要對元空間的大小做限制,因為它是占系統(tǒng)內(nèi)存,如果無限大,不僅會影響系統(tǒng)其它應(yīng)用的使用,嚴(yán)重的可能會導(dǎo)致系統(tǒng)崩潰。

2、JDK1.7時字符串常量池和靜態(tài)變量存儲在哪里

JDK1.7時永久代的部分?jǐn)?shù)據(jù)已經(jīng)從Java的永久代中轉(zhuǎn)移到了堆中,如:符號引用、字符串常量池

3、為什么Hotspot在JDK1.8要用元空間替換永久代

1、避免OOM:

因為方法區(qū)主要是存儲類的相關(guān)信息(包括類的字節(jié)碼文件),雖然永久代可以使用PerSize和MaxPerSize等參數(shù)設(shè)置永久代的空間大小,但隨著ASM、Cglib等動態(tài)生成字節(jié)碼技術(shù)的出現(xiàn)可以修改對象字節(jié)碼信息后,無法控制類信息的大小,很容易導(dǎo)致永久代內(nèi)存溢出的問題。

JDK1.8使用了元空間替換永久代,因為元空間是使用系統(tǒng)內(nèi)存,由系統(tǒng)的實際可用空間來控制,在一定程度上可以避免OOM的出現(xiàn),但是,也需要通過指定MaxMetaspaceSize等參數(shù)來控制大小。

2、提高GC性能:

永久代的垃圾收集是和老年代捆綁在一起的,所以無論兩者誰滿了,都會觸發(fā)永久代和老年代的垃圾收集。

使用元空間替換后,簡化了Full GC,減少了GC的時間(因為GC時不需要再掃描永久代中的數(shù)據(jù)),提高了GC的性能。在元空間中,只有少量指針指向堆如類的元數(shù)據(jù)中指向class對象的指針。

3、Hotspot和JRockit合并:

這個是官方給出的原因,永久代只是Hotspot虛擬機中存在的概念,JRockit中并沒有這個說法,JDK8需要整合Hotspot和JRockit,所以廢棄了永久代,引入了元空間。

4、認(rèn)識元空間

(一): 特點

1、保證了類和相關(guān)元數(shù)據(jù)的生命周期和類加載器保持一致。

2、加載器都有自己專門的存儲空間

3、節(jié)省了GC掃描和壓縮的時間

4、對象在元空間的位置是固定的

5、不會單獨回收某個類,如果GC發(fā)現(xiàn)某個類加載器不再存活了,則會將相關(guān)的空間整個都回收掉

(二): 存在的問題

1、元空間采用組塊分配的方式,具體的區(qū)塊大小是由類加載器決定的,但是因為類的信息并不是固定大小的,可能會導(dǎo)致非配的空間區(qū)塊和類需要的區(qū)塊大小不一致,這種情況會導(dǎo)致碎片的出現(xiàn)。

2、元空間虛擬機不支持壓縮操作.

六:程序計數(shù)器(Program Counter Register)

線程私有的,是占據(jù)著一塊小的內(nèi)存空間,作用是讀取下一條需要執(zhí)行的字節(jié)碼指令。它也是JVM規(guī)范中規(guī)定沒有OutOfMemoryError出現(xiàn)的區(qū)域。

它被設(shè)計出來的目的,是為了讓多線程情況下的JAVA程序每個線程都能夠正常的工作,每個線程都有自己的程序計數(shù)器,用于保存線程的執(zhí)行情況,這樣在進行線程切換的時候就可以在上次執(zhí)行的基礎(chǔ)上繼續(xù)執(zhí)行了。

簡單的舉例: 當(dāng)你正在用手機看著不可告人的小視頻,就在緊張刺激的時候,突然老王給你來個電話,此時的你被打斷了,臭罵老王一頓后,你肯定想接著看剛剛沒看完的進度而不是從頭看起,此刻,它是怎么知道你剛剛看到哪里了,程序計數(shù)器就起了作用,它負(fù)責(zé)管理進度,可以讓你在線程切換后還能知道之前的執(zhí)行位置(刺激吧)。

七:棧(Stack)

線程私有的,生命周期和線程一樣,在JAVA語言中,它是用來描述方法的執(zhí)行內(nèi)存模型。

在一個方法開始執(zhí)行前會創(chuàng)建一個棧幀(Stack Frame)用于存儲方法中的局部變量表、動態(tài)鏈接、方法出口等信息。一個方法的開始執(zhí)行和執(zhí)行結(jié)束對應(yīng)著一個棧幀在虛擬機的入棧和出棧的過程。

八:本地方法棧(Native Method Stack)

PS: 本地方法(Native Mehtod)是外部提供給JAVA語言調(diào)用的一個接口,一般是使用C或者C++實現(xiàn)。

線程私有,和棧相類似,區(qū)別是棧執(zhí)行的是JAVA語言編寫的方法,而本地方法棧執(zhí)行的是調(diào)用Native接口提供的方法既JAVA調(diào)用非JAVA實現(xiàn)的接口

九: JVM和JMM的關(guān)系

在平常代碼開發(fā)或者面試中,經(jīng)常會聽到JVM(JAVA虛擬機)和JMM(JAVA內(nèi)存模型),我們總是容易混淆它們之前的關(guān)系,下面就來詳細(xì)解釋下他們的區(qū)別。

JMM(JAVA Memory Model)即JAVA內(nèi)存模型,在JSR133中指出JMM是用來定義一個 一致的、跨平臺 的內(nèi)存模型,它并不像JVM內(nèi)存結(jié)構(gòu)一樣是真實存在的,是一個緩存一致性協(xié)議,屏蔽了各種硬件和操作系統(tǒng)的訪問差異的,用于定義數(shù)據(jù)讀寫的規(guī)則。

一:內(nèi)存可見性

在JMM中,我們講多個線程通信的共享內(nèi)存稱為主內(nèi)存,在并發(fā)編程中的應(yīng)用每個線程都維護著一個自己的工作內(nèi)存,工作內(nèi)存中保存的數(shù)據(jù)是主內(nèi)存數(shù)據(jù)的副本,JMM則用于控制本地內(nèi)存和主內(nèi)存之間的數(shù)據(jù)數(shù)據(jù)交互。

在這里插入圖片描述

通過上面的圖片和分析可知,JMM內(nèi)存模型和JVM本質(zhì)上是沒有關(guān)聯(lián)的,但是,如果要強行關(guān)聯(lián)的話,主內(nèi)存實際上對應(yīng)的是JVM中堆中的對象實例數(shù)據(jù)部分,工作內(nèi)存則對應(yīng)的是JVM中的棧部分。

什么是JRE

JAVA運行時環(huán)境(Java Runtime Enviroment)可以將它看做是一個容器, JVM 是它的內(nèi)容。

JRE = JVM + Java Packages Classes(like util, math, lang, awt,swing etc)+runtime libraries。

什么是JDK

Java Development Kit 用作開發(fā), 包含了JRE, 編譯器和其他的工具(比如: JavaDoc,Java調(diào)試器), 可以讓開發(fā)者開發(fā)、編譯、執(zhí)行Java應(yīng)用程序。

學(xué)習(xí)分享,共勉

醉里挑燈看劍,夢回吹角連營”,半夜被面試官驚醒,才有這篇JVM知識,想要面試OFFER多,平時少不了下苦工,但是不推薦大家熬夜,身體才是革命的本錢。上面只展現(xiàn)了JVM,還整理了一些筆記文檔,包括Java基礎(chǔ),Spring,MyBatis,多線程并發(fā),設(shè)計模式,數(shù)據(jù)庫,Redis,算法與數(shù)據(jù)結(jié)構(gòu),分布式等

資料免費領(lǐng)取方式:點贊+點贊+點贊關(guān)注后,戳我GitHub主頁即可獲取文檔資料的免費領(lǐng)取方式和這份JVM學(xué)習(xí)腦圖(內(nèi)含很多筆記)??!

重要的事說三遍,點贊+點贊+點贊!

Java架構(gòu)進階資料展示

有面試復(fù)習(xí)資料還有整理了面試高頻問題的視頻解析和大咖架構(gòu)進階筆記

如果覺的文章對你有幫助,【記得點贊哦!】你的支持是我創(chuàng)作更加優(yōu)質(zhì)文章的動力,不枉費我半夜起來加班!有任何建議或者意見評論區(qū)見!

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

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

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