一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

面試阿里高級架構(gòu)師之JVM篇

 梅振億 2018-07-07

在任何Java面試當(dāng)中JVM的問題都是必不可少的一部分

面試阿里高級架構(gòu)師之JVM篇

(一)JVM 基礎(chǔ)知識

(1)Java 是如何實現(xiàn)跨平臺的?

注意:跨平臺的是 Java 程序,而不是 JVM。JVM 是用 C/C 開發(fā)的,是編譯后的機(jī)器碼,不能跨平臺,不同平臺下需要安裝不同版本的 JVM

答:我們編寫的 Java 源碼,編譯后會生成一種 .class 文件,稱為字節(jié)碼文件。Java 虛擬機(jī)(JVM)就是負(fù)責(zé)將字節(jié)碼文件翻譯成特定平臺下的機(jī)器碼然后運行,也就是說,只要在不同平臺上安裝對應(yīng)的 JVM,就可以運行字節(jié)碼文件,運行我們編寫的 Java 程序。

而這個過程,我們編寫的 Java 程序沒有做任何改變,僅僅是通過 JVM 這一 “中間層” ,就能在不同平臺上運行,真正實現(xiàn)了 “一次編譯,到處運行” 的目的。

面試阿里高級架構(gòu)師之JVM篇

(2)什么是 JVM ?

解析:不僅僅是基本概念,還有 JVM 的作用。

答:JVM,即 Java Virtual Machine,Java 虛擬機(jī)。它通過模擬一個計算機(jī)來達(dá)到一個計算機(jī)所具有的的計算功能。JVM 能夠跨計算機(jī)體系結(jié)構(gòu)來執(zhí)行 Java 字節(jié)碼,主要是由于 JVM 屏蔽了與各個計算機(jī)平臺相關(guān)的軟件或者硬件之間的差異,使得與平臺相關(guān)的耦合統(tǒng)一由 JVM 提供者來實現(xiàn)。

(3)JVM 由哪些部分組成?

解析:這是對 JVM 體系結(jié)構(gòu)的考察

答:JVM 的結(jié)構(gòu)基本上由 4 部分組成:

  • 類加載器,在 JVM 啟動時或者類運行時將需要的 class 加載到 JVM 中
  • 執(zhí)行引擎,執(zhí)行引擎的任務(wù)是負(fù)責(zé)執(zhí)行 class 文件中包含的字節(jié)碼指令,相當(dāng)于實際機(jī)器上的 CPU
  • 內(nèi)存區(qū),將內(nèi)存劃分成若干個區(qū)以模擬實際機(jī)器上的存儲、記錄和調(diào)度功能模塊,如實際機(jī)器上的各種功能的寄存器或者 PC 指針的記錄器等
  • 本地方法調(diào)用,調(diào)用 C 或 C 實現(xiàn)的本地方法的代碼返回結(jié)果

面試阿里高級架構(gòu)師之JVM篇

(4)類加載器是有了解嗎?

解析:底層原理的考察,其中涉及到類加載器的概念,功能以及一些底層的實現(xiàn)。

答:顧名思義,類加載器(class loader)用來加載 Java 類到 Java 虛擬機(jī)中。一般來說,Java 虛擬機(jī)使用 Java 類的方式如下:Java 源程序(.java 文件)在經(jīng)過 Java 編譯器編譯之后就被轉(zhuǎn)換成 Java 字節(jié)代碼(.class 文件)。

類加載器負(fù)責(zé)讀取 Java 字節(jié)代碼,并轉(zhuǎn)換成 java.lang.Class類的一個實例。每個這樣的實例用來表示一個 Java 類。通過此實例的 newInstance()方法就可以創(chuàng)建出該類的一個對象。實際的情況可能更加復(fù)雜,比如 Java 字節(jié)代碼可能是通過工具動態(tài)生成的,也可能是通過網(wǎng)絡(luò)下載的。

面試官:Java 虛擬機(jī)是如何判定兩個 Java 類是相同的?

答:Java 虛擬機(jī)不僅要看類的全名是否相同,還要看加載此類的類加載器是否一樣。只有兩者都相同的情況,才認(rèn)為兩個類是相同的。即便是同樣的字節(jié)代碼,被不同的類加載器加載之后所得到的類,也是不同的。比如一個 Java 類 com.example.Sample,編譯之后生成了字節(jié)代碼文件 Sample.class。兩個不同的類加載器 ClassLoaderA和 ClassLoaderB分別讀取了這個 Sample.class文件,并定義出兩個 java.lang.Class類的實例來表示這個類。這兩個實例是不相同的。對于 Java 虛擬機(jī)來說,它們是不同的類。試圖對這兩個類的對象進(jìn)行相互賦值,會拋出運行時異常 ClassCastException。

(5)類加載器是如何加載 class 文件的?

答:下圖所示是 ClassLoader 加載一個 class 文件到 JVM 時需要經(jīng)過的步驟:

面試阿里高級架構(gòu)師之JVM篇

第一個階段是找到 .class 文件并把這個文件包含的字節(jié)碼加載到內(nèi)存中

第二階段又可以分為三個步驟,分別是字節(jié)碼驗證、Class 類數(shù)據(jù)結(jié)構(gòu)分析及相應(yīng)的內(nèi)存分配和最后的符號表的鏈接

第三個階段是類中靜態(tài)屬性和初始化賦值,以及靜態(tài)塊的執(zhí)行等

面試官:能詳細(xì)講講嗎?

答:

1.加載

查找并加載類的二進(jìn)制數(shù)據(jù)加載時類加載過程的第一個階段,在加載階段,虛擬機(jī)需要完成以下三件事情:

  • 通過一個類的全限定名來獲取其定義的二進(jìn)制字節(jié)流。
  • 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)。
  • 在Java堆中生成一個代表這個類的 java.lang.Class 對象,作為對方法區(qū)中這些數(shù)據(jù)的訪問入口。

相對于類加載的其他階段而言,加載階段(準(zhǔn)確地說,是加載階段獲取類的二進(jìn)制字節(jié)流的動作)是可控性最強(qiáng)的階段,因為開發(fā)人員既可以使用系統(tǒng)提供的類加載器來完成加載,也可以自定義自己的類加載器來完成加載。

加載階段完成后,虛擬機(jī)外部的二進(jìn)制字節(jié)流就按照虛擬機(jī)所需的格式存儲在方法區(qū)之中,而且在Java堆中也創(chuàng)建一個 java.lang.Class類的對象,這樣便可以通過該對象訪問方法區(qū)中的這些數(shù)據(jù)。

2.連接

驗證:確保被加載的類的正確性

驗證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會危害虛擬機(jī)自身的安全。驗證階段大致會完成4個階段的檢驗動作:

  • 文件格式驗證:驗證字節(jié)流是否符合Class文件格式的規(guī)范;例如:是否以 0xCAFEBABE開頭、主次版本號是否在當(dāng)前虛擬機(jī)的處理范圍之內(nèi)、常量池中的常量是否有不被支持的類型。
  • 元數(shù)據(jù)驗證:對字節(jié)碼描述的信息進(jìn)行語義分析(注意:對比javac編譯階段的語義分析),以保證其描述的信息符合Java語言規(guī)范的要求;例如:這個類是否有父類,除了 java.lang.Object之外。
  • 字節(jié)碼驗證:通過數(shù)據(jù)流和控制流分析,確定程序語義是合法的、符合邏輯的。
  • 符號引用驗證:確保解析動作能正確執(zhí)行。

驗證階段是非常重要的,但不是必須的,它對程序運行期沒有影響,如果所引用的類經(jīng)過反復(fù)驗證,那么可以考慮采用 -Xverifynone 參數(shù)來關(guān)閉大部分的類驗證措施,以縮短虛擬機(jī)類加載的時間。

準(zhǔn)備:為類的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值

準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些內(nèi)存都將在方法區(qū)中分配。對于該階段有以下幾點需要注意:

  • ① 這時候進(jìn)行內(nèi)存分配的僅包括類變量(static),而不包括實例變量,實例變量會在對象實例化時隨著對象一塊分配在Java堆中。
  • ② 這里所設(shè)置的初始值通常情況下是數(shù)據(jù)類型默認(rèn)的零值(如0、0L、null、false等),而不是被在Java代碼中被顯式地賦予的值。

假設(shè)一個類變量的定義為: public static int value = 3;

那么變量value在準(zhǔn)備階段過后的初始值為 0,而不是 3,因為這時候尚未開始執(zhí)行任何 Java 方法,而把 value 賦值為 3 的public static指令是在程序編譯后,存放于類構(gòu)造器 <clinit>()方法之中的,所以把value賦值為3的動作將在初始化階段才會執(zhí)行。

這里還需要注意如下幾點:

  • 對基本數(shù)據(jù)類型來說,對于類變量(static)和全局變量,如果不顯式地對其賦值而直接使用,則系統(tǒng)會為其賦予默認(rèn)的零值,而對于局部變量來說,在使用前必須顯式地為其賦值,否則編譯時不通過。
  • 對于同時被static和final修飾的常量,必須在聲明的時候就為其顯式地賦值,否則編譯時不通過;而只被final修飾的常量則既可以在聲明時顯式地為其賦值,也可以在類初始化時顯式地為其賦值,總之,在使用前必須為其顯式地賦值,系統(tǒng)不會為其賦予默認(rèn)零值。
  • 對于引用數(shù)據(jù)類型reference來說,如數(shù)組引用、對象引用等,如果沒有對其進(jìn)行顯式地賦值而直接使用,系統(tǒng)都會為其賦予默認(rèn)的零值,即null。
  • 如果在數(shù)組初始化時沒有對數(shù)組中的各元素賦值,那么其中的元素將根據(jù)對應(yīng)的數(shù)據(jù)類型而被賦予默認(rèn)的零值。
  • ③ 如果類字段的字段屬性表中存在 ConstantValue 屬性,即同時被 final 和 static 修飾,那么在準(zhǔn)備階段變量 value 就會被初始化為 ConstValue 屬性所指定的值。

假設(shè)上面的類變量 value 被定義為: public static final int value = 3;

編譯時 Javac 將會為 value 生成 ConstantValue 屬性,在準(zhǔn)備階段虛擬機(jī)就會根據(jù) ConstantValue 的設(shè)置將 value 賦值為 3。我們可以理解為 static final 常量在編譯期就將其結(jié)果放入了調(diào)用它的類的常量池中

解析:把類中的符號引用轉(zhuǎn)換為直接引用

解析階段是虛擬機(jī)將常量池內(nèi)的符號引用替換為直接引用的過程,解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點限定符7類符號引用進(jìn)行。符號引用就是一組符號來描述目標(biāo),可以是任何字面量。

直接引用就是直接指向目標(biāo)的指針、相對偏移量或一個間接定位到目標(biāo)的句柄。

3.初始化

初始化,為類的靜態(tài)變量賦予正確的初始值,JVM負(fù)責(zé)對類進(jìn)行初始化,主要對類變量進(jìn)行初始化。在Java中對類變量進(jìn)行初始值設(shè)定有兩種方式:

  • ① 聲明類變量是指定初始值
  • ② 使用靜態(tài)代碼塊為類變量指定初始值

JVM初始化步驟

  • 1、假如這個類還沒有被加載和連接,則程序先加載并連接該類
  • 2、假如該類的直接父類還沒有被初始化,則先初始化其直接父類
  • 3、假如類中有初始化語句,則系統(tǒng)依次執(zhí)行這些初始化語句

類初始化時機(jī):只有當(dāng)對類的主動使用的時候才會導(dǎo)致類的初始化,類的主動使用包括以下六種:

  • 創(chuàng)建類的實例,也就是new的方式
  • 訪問某個類或接口的靜態(tài)變量,或者對該靜態(tài)變量賦值
  • 調(diào)用類的靜態(tài)方法
  • 反射(如 Class.forName(“com.shengsiyuan.Test”))
  • 初始化某個類的子類,則其父類也會被初始化
  • Java虛擬機(jī)啟動時被標(biāo)明為啟動類的類( JavaTest),直接使用 java.exe命令來運行某個主類

結(jié)束生命周期

在如下幾種情況下,Java虛擬機(jī)將結(jié)束生命周期

  • 執(zhí)行了 System.exit()方法
  • 程序正常執(zhí)行結(jié)束
  • 程序在執(zhí)行過程中遇到了異?;蝈e誤而異常終止
  • 由于操作系統(tǒng)出現(xiàn)錯誤而導(dǎo)致Java虛擬機(jī)進(jìn)程終止

(6)雙親委派模型(Parent Delegation Model)?

解析:類的加載過程采用雙親委派機(jī)制,這種機(jī)制能更好的保證 Java 平臺的安全性

答:類加載器 ClassLoader 是具有層次結(jié)構(gòu)的,也就是父子關(guān)系,其中,Bootstrap 是所有類加載器的父親,如下圖所示:

面試阿里高級架構(gòu)師之JVM篇

該模型要求除了頂層的 Bootstrap class loader 啟動類加載器外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。子類加載器和父類加載器不是以繼承(Inheritance)的關(guān)系來實現(xiàn),而是通過組合(Composition)關(guān)系來復(fù)用父加載器的代碼。每個類加載器都有自己的命名空間(由該加載器及所有父類加載器所加載的類組成,在同一個命名空間中,不會出現(xiàn)類的完整名字(包括類的包名)相同的兩個類;在不同的命名空間中,有可能會出現(xiàn)類的完整名字(包括類的包名)相同的兩個類)

面試官:雙親委派模型的工作過程?

答:

1.當(dāng)前 ClassLoader 首先從自己已經(jīng)加載的類中查詢是否此類已經(jīng)加載,如果已經(jīng)加載則直接返回原來已經(jīng)加載的類。

每個類加載器都有自己的加載緩存,當(dāng)一個類被加載了以后就會放入緩存,

等下次加載的時候就可以直接返回了。

2.當(dāng)前 ClassLoader 的緩存中沒有找到被加載的類的時候,委托父類加載器去加載,父類加載器采用同樣的策略,首先查看自己的緩存,然后委托父類的父類去加載,一直到 bootstrap ClassLoader.

當(dāng)所有的父類加載器都沒有加載的時候,再由當(dāng)前的類加載器加載,并將其放入它自己的緩存中,以便下次有加載請求的時候直接返回。

面試官:為什么這樣設(shè)計呢?

解析:這是對于使用這種模型來組織累加器的好處

答:主要是為了安全性,避免用戶自己編寫的類動態(tài)替換 Java 的一些核心類,比如 String,同時也避免了重復(fù)加載,因為 JVM 中區(qū)分不同類,不僅僅是根據(jù)類名,相同的 class 文件被不同的 ClassLoader 加載就是不同的兩個類,如果相互轉(zhuǎn)型的話會拋java.lang.ClassCaseException.

(二)JVM 內(nèi)存管理

(1)JVM 內(nèi)存劃分:

面試阿里高級架構(gòu)師之JVM篇

答:

  1. 方法區(qū)(線程共享):各個線程共享的一個區(qū)域,用于存儲虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。雖然 Java 虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個邏輯部分,但是它卻又一個別名叫做 Non-Heap(非堆),目的應(yīng)該是與 Java 堆區(qū)分開來。
  2. 運行時常量池:是方法區(qū)的一部分,用于存放編譯器生成的各種字面量和符號引用。
  3. 堆內(nèi)存(線程共享):所有線程共享的一塊區(qū)域,垃圾收集器管理的主要區(qū)域。目前主要的垃圾回收算法都是分代收集算法,所以 Java 堆中還可以細(xì)分為:新生代和老年代;再細(xì)致一點的有 Eden 空間、From Survivor 空間、To Survivor 空間等,默認(rèn)情況下新生代按照8:1:1的比例來分配。根據(jù) Java 虛擬機(jī)規(guī)范的規(guī)定,Java 堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可,就像我們的磁盤一樣。
  4. 程序計數(shù)器: Java 線程私有,類似于操作系統(tǒng)里的 PC 計數(shù)器,它可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。如果線程正在執(zhí)行的是一個 Java 方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是 Native 方法,這個計數(shù)器值則為空(Undefined)。此內(nèi)存區(qū)域是唯一一個在 Java 虛擬機(jī)規(guī)范中沒有規(guī)定任何 OutOfMemoryError 情況的區(qū)域。
  5. 虛擬機(jī)棧(棧內(nèi)存):Java線程私有,虛擬機(jī)展描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的時候,都會創(chuàng)建一個棧幀用于存儲局部變量、操作數(shù)、動態(tài)鏈接、方法出口等信息;每個方法調(diào)用都意味著一個棧幀在虛擬機(jī)棧中入棧到出棧的過程;
  6. 本地方法棧 :和Java虛擬機(jī)棧的作用類似,區(qū)別是該區(qū)域為 JVM 提供使用 native 方法的服務(wù)

(2)Java 的內(nèi)存模型:

答:

面試阿里高級架構(gòu)師之JVM篇

Java 虛擬機(jī)規(guī)范中試圖定義一種 Java 內(nèi)存模型(Java Memory Model, JMM)來屏蔽掉各層硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實現(xiàn)讓 Java 程序在各種平臺下都能達(dá)到一致的內(nèi)存訪問效果。

Java 內(nèi)存模型規(guī)定了所有的變量都存儲在主內(nèi)存(Main Memory)中。每條線程還有自己的工作內(nèi)存(Working Memory),線程的工作內(nèi)存中保存了被該線程使用到的變量的主內(nèi)存副本拷貝,線程對變量的所有操作(讀取、賦值等)都必須在主內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存中的變量。不同的線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間的變量值的傳遞均需要通過主內(nèi)存來完成,線程、主內(nèi)存、工作內(nèi)存三者的關(guān)系如上圖。

那如何學(xué)習(xí)才能快速入門并精通呢?

當(dāng)真正開始學(xué)習(xí)的時候難免不知道從哪入手,導(dǎo)致效率低下影響繼續(xù)學(xué)習(xí)的信心。

但最重要的是不知道哪些技術(shù)需要重點掌握,學(xué)習(xí)時頻繁踩坑,最終浪費大量時間,所以有一套實用的視頻課程用來跟著學(xué)習(xí)是非常有必要的。

為了讓學(xué)習(xí)變得輕松、高效,今天給大家免費分享一套阿里架構(gòu)師傳授的一套教學(xué)資源。幫助大家在成為架構(gòu)師的道路上披荊斬棘。

這套視頻課程詳細(xì)講解了(Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu))等這些成為架構(gòu)師必備的內(nèi)容!

而且還把框架需要用到的各種程序進(jìn)行了打包,根據(jù)基礎(chǔ)視頻可以讓你輕松搭建分布式框架環(huán)境,像在企業(yè)生產(chǎn)環(huán)境一樣進(jìn)行學(xué)習(xí)和實踐。

面試阿里高級架構(gòu)師之JVM篇

后臺私信回復(fù)“架構(gòu)” 就可以馬上免費獲得這套價值一萬八的內(nèi)部教材!

最后,做一個愛思考,懂思考,會思考的程序員。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    果冻传媒在线观看免费高清| 丰满少妇高潮一区二区| 人人妻在人人看人人澡| 国产成人av在线免播放观看av| 一区二区欧美另类稀缺| 99国产高清不卡视频| 亚洲国产91精品视频| 亚洲夫妻性生活免费视频| 在线免费视频你懂的观看| 大香蕉久草网一区二区三区 | 五月婷婷六月丁香亚洲| 中文字幕一区二区免费| 我想看亚洲一级黄色录像| 国产免费黄片一区二区| 久久久精品区二区三区| 中文字幕亚洲视频一区二区| 亚洲天堂一区在线播放| 草草草草在线观看视频| 日本亚洲精品在线观看| 伊人久久青草地婷婷综合| 国产视频福利一区二区| 日韩一区二区三区18| 国产目拍亚洲精品区一区| 尹人大香蕉一级片免费看| 亚洲一区二区三区熟女少妇| 日韩高清毛片免费观看| 成人综合网视频在线观看| 日韩一区中文免费视频| 日韩一级一片内射视频4k| 成人午夜视频精品一区| 日韩人妻欧美一区二区久久| a久久天堂国产毛片精品| 亚洲欧美日本国产不卡| 激情三级在线观看视频| 久久99国产精品果冻传媒| 操白丝女孩在线观看免费高清 | 成人免费在线视频大香蕉| 中国美女草逼一级黄片视频| 亚洲深夜精品福利一区| 91精品视频免费播放| 欧美在线观看视频三区|