Android JVM面試專題:阿里100%會問到的JVM,20道靈魂質問面試題解析,讓你吊打面試官

南方吳彥祖_藍斯發表於2020-10-26

靈魂4問:

1、你知道面試官為什麼要問你JVM原理嗎?
2、你知道被問的原理包含哪些嗎?
3、你知道這些原理是如何知道我們程式碼實踐開發的嗎?
4、你知道如何從架構層面最佳化我們的效能嗎?

Android應用程式開發是以Java語言為基礎的(kotlin也是對java的封裝),你做Android開發的時候經常遇到的各種問題,其實都是因為java基礎沒打好,其中最重要的無疑是JVM!Java本身有一個很好的管理記憶體的機制—— GC。然而,GC卻經常會遇到無法回收的記憶體,即記憶體洩漏,記憶體抖動,結果可能是OOM。

我們普通的開發涉及不到JVM,然而App效能與它有著千絲萬縷的聯絡,每個App都會有自己的art虛擬機器,甚至每一個程式都是有自己獨立的虛擬機器,記憶體的回收是由虛擬機器來管理的,GC回收演算法怎樣,adj記憶體管理, 這一切都基於虛擬機器

然而很多半路出家、甚至科班出身、工作好幾年的安卓開發者,對JVM的核心原理並不瞭解。接下來這些大廠關於JVM的面試題,你能不能答上來?

1. 記憶體模型以及分割槽,需要詳細到每個區放什麼?

JVM 分為堆區和棧區,還有方法區,初始化的物件放在堆裡面,引用放在棧裡面,class 類資訊常量池(static 常量和 static 變數)等放在方法區new:

  • 方法區:主要是儲存類資訊,常量池(static 常量和 static 變數),編譯後的程式碼(位元組碼)等資料
  • 堆:初始化的物件,成員變數 (那種非 static 的變數),所有的物件例項和陣列都要在堆上分配
  • 棧:棧的結構是棧幀組成的,呼叫一個方法就壓入一幀,幀上面儲存區域性變數表,運算元棧,方法出口等資訊,區域性變數表存放的是 8 大基礎型別加上一個應用型別,所以還是一個指向地址的指標
  • 本地方法棧:主要為 Native 方法服務
  • 程式計數器:記錄當前執行緒執行的行號

2. 堆裡面的分割槽:Eden,survival (from+ to),老年代,各自的特點。

堆裡面分為新生代和老生代(java8 取消了永久代,採用了 Metaspace),新生代包含 Eden+Survivor 區,survivor 區裡面分為 from 和 to 區,記憶體回收時,如果用的是複製演算法,從 from 複製到 to,當經過一次或者多次 GC 之後,存活下來的物件會被移動到老年區,當 JVM 記憶體不夠用的時候,會觸發 Full GC,清理 JVM 老年區當新生區滿了之後會觸發 YGC,先把存活的物件放到其中一個 Survice區,然後進行垃圾清理。

因為如果僅僅清理需要刪除的物件,這樣會導致記憶體碎片,因此一般會把 Eden 進行完全的清理,然後整理記憶體。那麼下次 GC 的時候,就會使用下一個 Survive,這樣迴圈使用。如果有特別大的物件,新生代放不下,就會使用老年代的擔保,直接放到老年代裡面。因為 JVM 認為,一般大物件的存活時間一般比較久遠。

3. 物件建立方法,物件的記憶體分配,物件的訪問定位。

new 一個物件

4. GC 的兩種判定方法:

引用計數法:指的是如果某個地方引用了這個物件就+1,如果失效了就-1,當為 0 就會回收但是 JVM 沒有用這種方式,因為無法判定相互迴圈引用(A 引用 B,B 引用 A)的情況

引用鏈法: 透過一種 GC ROOT 的物件(方法區中靜態變數引用的物件等-static 變數)來判斷,如果有一條鏈能夠到達 GC ROOT 就說明,不能到達 GC ROOT 就說明可以回收

5. SafePoint 是什麼

比如 GC 的時候必須要等到 Java 執行緒都進入到 safepoint 的時候 VMThread 才能開始執行 GC,

  1. 迴圈的末尾 (防止大迴圈的時候一直不進入 safepoint,而其他執行緒在等待它進入safepoint)
  2. 方法返回前
  3. 呼叫方法的 call 之後
  4. 丟擲異常的位置

6. GC 的三種收集方法:標記清除、標記整理、複製演算法的原理與特點,分別用在什麼地方,如果讓你最佳化收集方法,有什麼思路?

先標記,標記完畢之後再清除,效率不高,會產生碎片

複製演算法:分為 8:1 的 Eden 區和 survivor 區,就是上面談到的 YGC
標記整理:標記完畢之後,讓所有存活的物件向一端移動

7. GC 收集器有哪些?CMS 收集器與 G1 收集器的特點。

並行收集器:序列收集器使用一個單獨的執行緒進行收集,GC 時服務有停頓時間
序列收集器:次要回收中使用多執行緒來執行

CMS 收集器是基於“標記—清除”演算法實現的,經過多次標記才會被清除

G1 從整體來看是基於 “標記—整理”演算法實現的收集器,從區域性(兩個 Region 之間)上來看是基於“複製”演算法實現的

8. Minor GC 與 Full GC 分別在什麼時候發生?

新生代記憶體不夠用時候發生 MGC 也叫 YGC,JVM 記憶體不夠的時候發生 FGC

9. 幾種常用的記憶體除錯工具:jmap、jstack、jconsole、jhat

jstack 可以看當前棧的情況,jmap 檢視記憶體,jhat 進行 dump 堆的資訊

mat(eclipse 的也要了解一下)

10. 類載入的幾個過程:

載入、驗證、準備、解析、初始化。然後是使用和解除安裝了

透過全限定名來載入生成 class 物件到記憶體中,然後進行驗證這個 class 檔案,包括檔案格式校驗、後設資料驗證,位元組碼校驗等。準備是對這個物件分配記憶體。解析是將符號引用轉化為直接引用(指標引用),初始化就是開始執行構造器的程式碼

11.JVM 記憶體分哪幾個區,每個區的作用是什麼?

java 虛擬機器主要分為以下一個區:

方法區:

  1. 有時候也成為永久代,在該區內很少發生垃圾回收,但是並不代表不發生 GC,在這裡進行的 GC 主要是對方法區裡的常量池和對型別的解除安裝
  2. 方法區主要用來儲存已被虛擬機器載入的類的資訊、常量、靜態變數和即時編譯器編譯後的程式碼等資料。
  3. 該區域是被執行緒共享的。
  4. 方法區裡有一個執行時常量池,用於存放靜態編譯產生的字面量和符號引用。該常量池具有動態性,也就是說常量並不一定是編譯時確定,執行時生成的常量也會存在這個常量池中。

虛擬機器棧:

  1. 虛擬機器棧也就是我們平常所稱的棧記憶體,它為 java 方法服務,每個方法在執行的時候都會建立一個棧幀,用於儲存區域性變數表、運算元棧、動態連結和方法出口等資訊。

  2. 虛擬機器棧是執行緒私有的,它的生命週期與執行緒相同。

  3. 區域性變數表裡儲存的是基本資料型別、returnAddress 型別(指向一條位元組碼指令的地址)和物件引用,這個物件引用有可能是指向物件起始地址的一個指標,也有可能是代表物件的控制程式碼或者與物件相關聯的位置。區域性變數所需的記憶體空間在編譯器間確定

4.運算元棧的作用主要用來儲存運算結果以及運算的運算元,它不同於區域性變數表透過索引來訪問,而是壓棧和出棧的方式

5.每個棧幀都包含一個指向執行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支援方法呼叫過程中的動態連線.動態連結就是將常量池中的符號引用在執行期轉化為直接引用。

本地方法棧

本地方法棧和虛擬機器棧類似,只不過本地方法棧為 Native 方法服務。

java 堆是所有執行緒所共享的一塊記憶體,在虛擬機器啟動時建立,幾乎所有的物件例項都在這裡建立,因此該區域經常發生垃圾回收操作。

程式計數器

記憶體空間小,位元組碼直譯器工作時透過改變這個計數值可以選取下一條需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理和執行緒恢復等功能都需要依賴這個計數器完成。該記憶體區域是唯一一個 java 虛擬機器規範沒有規定任何 OOM 情況的區域。

12.如和判斷一個物件是否存活?(或者 GC 物件的判定方

法)

判斷一個物件是否存活有兩種方法:

1. 引用計數法

所謂引用計數法就是給每一個物件設定一個引用計數器,每當有一個地方引用這個物件時,就將計數器加一,引用失效時,計數器就減一。當一個物件的引用計數器為零時,說明此物件沒有被引用,也就是“死物件”,將會被垃圾回收。

引用計數法有一個缺陷就是無法解決迴圈引用問題,也就是說當物件 A 引用物件 B,物件B 又引用者物件 A,那麼此時 A,B 物件的引用計數器都不為零,也就造成無法完成垃圾回收,所以主流的虛擬機器都沒有采用這種演算法。

2.可達性演算法(引用鏈法)

該演算法的思想是:從一個被稱為 GC Roots 的物件開始向下搜尋,如果一個物件到 GC Roots 沒有任何引用鏈相連時,則說明此物件不可用。

在 java 中可以作為 GC Roots 的物件有以下幾種:

  • 虛擬機器棧中引用的物件
  • 方法區類靜態屬性引用的物件
  • 方法區常量池引用的物件
  • 本地方法棧 JNI 引用的物件

雖然這些演算法可以判定一個物件是否能被回收,但是當滿足上述條件時,一個物件比 不一定會被回收。當一個物件不可達 GC Root 時,這個物件並 不會立馬被回收,而是出於一個死緩的階段,若要被真正的回收需要經歷兩次標記如果物件在可達性分析中沒有與 GC Root 的引用鏈,那麼此時就會被第一次標記並且進行一次篩選,篩選的條件是是否有必要執行 finalize()方法。當物件沒有覆蓋 finalize()方法或者已被虛擬機器呼叫過,那麼就認為是沒必要的。

如果該物件有必要執行 finalize()方法,那麼這個物件將會放在一個稱為 F-Queue 的對佇列中,虛擬機器會觸發一個 Finalize()執行緒去執行,此執行緒是低優先順序的,並且虛擬機器不會承諾一直等待它執行完,這是因為如果 finalize()執行緩慢或者發生了死鎖,那麼就會造成 F-Queue 佇列一直等待,造成了記憶體回收系統的崩潰。GC 對處於 F-Queue 中的物件進行第二次被標記,這時,該物件將被移除”即將回收”集合,等待回收。

13.簡述 java 垃圾回收機制?

在 java 中,程式設計師是不需要顯示的去釋放一個物件的記憶體的,而是由虛擬機器自行執行。在JVM 中,有一個垃圾回收執行緒,它是低優先順序的,在正常情況下是不會執行的,只有在虛擬機器空閒或者當前堆記憶體不足時,才會觸發執行,掃面那些沒有被任何引用的物件,並將它們新增到要回收的集合中,進行回收。

14.java 中垃圾收集的方法有哪些?

1. 標記-清除:

這是垃圾收集演算法中最基礎的,根據名字就可以知道,它的思想就是標記哪些要被回收的物件,然後統一回收。這種方法很簡單,但是會有兩個主要問題:

1)效率不高,標記和清除的效率都很低;
2)會產生大量不連續的記憶體碎片,導致以後程式在分配較大的物件時,由於沒有充足的連續記憶體而提前觸發一次 GC 動作。

2. 複製演算法:
為了解決效率問題,複製演算法將可用記憶體按容量劃分為相等的兩部分,然後每次只使用其中的一塊,當一塊記憶體用完時,就將還存活的物件複製到第二塊記憶體上,然後一次性清楚完第一塊記憶體,再將第二塊上的物件複製到第一塊。但是這種方式,記憶體的代價太高,每次基本上都要浪費一般的記憶體。

於是將該演算法進行了改進,記憶體區域不再是按照 1:1 去劃分,而是將記憶體劃分為8:1:1 三部分,較大那份記憶體交 Eden 區,其餘是兩塊較小的記憶體區叫 Survior 區。每次都會優先使用 Eden 區,若 Eden 區滿,就將物件複製到第二塊記憶體區上,然後清除 Eden 區,如果此時存活的物件太多,以至於 Survivor 不夠時,會將這些物件透過分配擔保機制複製到老年代中。(java 堆又分為新生代和老年代)

3. 標記-整理:

該演算法主要是為了解決標記-清除,產生大量記憶體碎片的問題;當物件存活率較高時,也解決了複製演算法的效率問題。它的不同之處就是在清除物件的時候現將可回收物件移動到一端,然後清除掉端邊界以外的物件,這樣就不會產生記憶體碎片了。

4. 分代收集:

現在的虛擬機器垃圾收集大多采用這種方式,它根據物件的生存週期,將堆分為新生代和老年代。在新生代中,由於物件生存期短,每次回收都會有大量物件死去,那麼這時就採用 複製演算法。老年代裡的物件存活率較高,沒有額外的空間進行分配擔保,所以可以使用 標記-整理 或者  標記-清除

15.java 記憶體模型

java 記憶體模型(JMM)是執行緒間通訊的控制機制.JMM 定義了主記憶體和執行緒之間抽象關係。

執行緒之間的共享變數儲存在主記憶體(main memory)中,每個執行緒都有一個私有的本地記憶體(local memory),本地記憶體中儲存了該執行緒以讀/寫共享變數的副本。本地記憶體是JMM 的一個抽象概念,並不真實存在。它涵蓋了快取,寫緩衝區,暫存器以及其他的硬體和編譯器最佳化。

Java 記憶體模型的抽象示意圖如下:

Android JVM面試專題:阿里100%會問到的JVM,20道靈魂質問面試題解析,讓你吊打面試官

從上圖來看,執行緒 A 與執行緒 B 之間如要通訊的話,必須要經歷下面 2 個步驟:

  1. 首先,執行緒 A 把本地記憶體 A 中更新過的共享變數重新整理到主記憶體中去。

  2. 然後,執行緒 B 到主記憶體中去讀取執行緒 A 之前已更新過的共享變數。

16.java 類載入過程?

java 類載入需要經歷一下 7 個過程:

載入

載入時類載入的第一個過程,在這個階段,將完成一下三件事情:

  1. 透過一個類的全限定名獲取該類的二進位制流。
  2. 將該二進位制流中的靜態儲存結構轉化為方法去執行時資料結構。
  3. 在記憶體中生成該類的 Class 物件,作為該類的資料訪問入口。

驗證

驗證的目的是為了確保 Class 檔案的位元組流中的資訊不回危害到虛擬機器.在該階段主要完成以下四鍾驗證:

  1. 檔案格式驗證:驗證位元組流是否符合 Class 檔案的規範,如主次版本號是否在當前虛擬機器範圍內,常量池中的常量是否有不被支援的型別。
  2. 後設資料驗證:對位元組碼描述的資訊進行語義分析,如這個類是否有父類,是否整合了不被繼承的類等。
  3. 位元組碼驗證:是整個驗證過程中最複雜的一個階段,透過驗證資料流和控制流的分析,確定程式語義是否正確,主要針對方法體的驗證。如:方法中的型別轉換是否正確,跳轉指令是否正確等。
  4. 符號引用驗證:這個動作在後面的解析過程中發生,主要是為了確保解析動作能正確執行。

準備

準備階段是為類的靜態變數分配記憶體並將其初始化為預設值,這些記憶體都將在方法區中進行分配。準備階段不分配類中的例項變數的記憶體,例項變數將會在物件例項化時隨著物件一起分配在 Java 堆中。

public static int value=123;//在準備階段 value 初始值為 0 。在初始化階段才會變為 123 。

解析

該階段主要完成符號引用到直接引用的轉換動作。解析動作並不一定在初始化動作完成之前,也有可能在初始化之後。

初始化

初始化時類載入的最後一步,前面的類載入過程,除了在載入階段使用者應用程式可以透過自定義類載入器參與之外,其餘動作完全由虛擬機器主導和控制。到了初始化階段,才真正開始執行類中定義的 Java 程式程式碼。

17. 簡述 java 類載入機制?

虛擬機器把描述類的資料從 Class 檔案載入到記憶體,並對資料進行校驗,解析和初始化,最終形成可以被虛擬機器直接使用的 java 型別。

18. 類載入器雙親委派模型機制?

當一個類收到了類載入請求時,不會自己先去載入這個類,而是將其委派給父類,由父類去載入,如果此時父類不能載入,反饋給子類,由子類去完成類的載入。

19.什麼是類載入器,類載入器有哪些?

實現透過類的許可權定名獲取該類的二進位制位元組流的程式碼塊叫做類載入器。

主要有一下四種類載入器:

1. 啟動類載入器(Bootstrap ClassLoader)用來載入 java 核心類庫,無法被 java 程式直接引用。
2. 擴充套件類載入器(extensions class loader):它用來載入 Java 的擴充套件庫。Java 虛擬機器的實現會提供一個擴充套件庫目錄。該類載入器在此目錄裡面查詢並載入 Java 類。
3. 系統類載入器(system class loader):它根據 Java 應用的類路徑(CLASSPATH)來載入 Java 類。一般來說,Java 應用的類都是由它來完成載入的。可以通ClassLoader.getSystemClassLoader()來獲取它。
4. 使用者自定義類載入器,透過繼承 java.lang.ClassLoader 類的方式實現。

20.簡述 java 記憶體分配與回收策率以及 Minor GC 和Major GC

  1. 物件優先在堆的 Eden 區分配。

  2. 大物件直接進入老年代。

  3. 長期存活的物件將直接進入老年代。當 Eden 區沒有足夠的空間進行分配時,虛擬機器會執行一次 Minor GC.Minor Gc 通常發生在新生代的 Eden 區,在這個區的物件生存期短,往往發生 Gc 的頻率較高,
    回收速度比較快;Full Gc/Major GC 發生在老年代,一般情況下,觸發老年代 GC的時候不會觸發 Minor GC,但是透過配置,可以在 Full GC 之前進行一次 MinorGC 這樣可以加快老年代的回收速度。

Android及JVM學習資源

其實客戶端開發的知識點就那麼多,面試問來問去還是那麼點東西。所以面試沒有其他的訣竅,只看你對這些知識點準備的充分程度。so,出去面試時先看看自己複習到了哪個階段就好。

這裡再分享一下我面試期間的複習路線:(以下體系的複習資料是我從各路大佬收集整理好的)

《Android開發七大模組核心知識筆記》

Android JVM面試專題:阿里100%會問到的JVM,20道靈魂質問面試題解析,讓你吊打面試官
Android JVM面試專題:阿里100%會問到的JVM,20道靈魂質問面試題解析,讓你吊打面試官
Android JVM面試專題:阿里100%會問到的JVM,20道靈魂質問面試題解析,讓你吊打面試官

《379頁Android開發面試寶典》

歷時半年,我們整理了這份市面上最全面的安卓面試題解析大全
包含了騰訊、百度、小米、阿里、樂視、美團、58、360、新浪、搜狐等一線網際網路公司面試被問到的題目。熟悉本文中列出的知識點會大大增加透過前兩輪技術面試的機率。

如何使用它?

1.可以透過目錄索引直接翻看需要的知識點,查漏補缺。
2.五角星數表示面試問到的頻率,代表重要推薦指數

Android JVM面試專題:阿里100%會問到的JVM,20道靈魂質問面試題解析,讓你吊打面試官

《JVM核心知識點》

為了幫助大家深刻理解JVM核心原理和麵試相關知識,特地整理分享了JVM相關的核心知識點:

  1. Java記憶體模型
  2. GC機制
  3. 類載入
Android JVM面試專題:阿里100%會問到的JVM,20道靈魂質問面試題解析,讓你吊打面試官
Android JVM面試專題:阿里100%會問到的JVM,20道靈魂質問面試題解析,讓你吊打面試官

資料太多,全部展示會影響篇幅,暫時就先列舉這些部分截圖,以上資源均免費分享,以上內容均放在了開源專案: github  中已收錄,大家可以自行獲取(或者 關注主頁掃描加微信獲取)。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983917/viewspace-2730061/,如需轉載,請註明出處,否則將追究法律責任。

相關文章