天天看點

面試官:我就問了一個JVM,沒想到他能吹半個小時

前言:

作為 Java 的從業者,在找工作的時候,一定會被問及關于 JVM 相關的知識。 JVM知識的掌握程度,在很多面試官眼裡是候選人技術深度的一個重要評判标準。 在這裡我們将詳細的整理常見的 JVM 面試題目,并給出标準答案,提供給大家學習參考。等大家面試的時候,希望能對面試官吹個半個小時,如果真是這樣,我想說:牛皮啊!

另外本人整理收藏了20年多家公司面試知識點整理 ,以及各種Java核心知識點免費分享給大家,想要資料的話請點1065653031暗号CSDN。

面試官:我就問了一個JVM,沒想到他能吹半個小時

目錄

    • 前言:
    • 1、記憶體模型以及分區,需要詳細到每個區放什麼。
    • 2. 堆裡面的分區:Eden,survival (from+ to),老年代,各自的特點。
    • 3. GC 的兩種判定方法
    • 4. Minor GC 與 Full GC 分别在什麼時候發生?
    • 5. 類加載的幾個過程:
    • 6.JVM 記憶體分哪幾個區,每個區的作用是什麼
    • 7.如和判斷一個對象是否存活?(或者 GC 對象的判定方法)
    • 8.java 中垃圾收集的方法有哪些?
    • 9.什麼是類加載器,類加載器有哪些?
    • 10. 類加載器雙親委派模型機制?
    • 11.什麼情況下會發生棧記憶體溢出?
    • 12.怎麼打破雙親委派模型?
    • 13.強引用、軟應用、弱引用、虛引用的差別?
    • 總結:
面試官:我就問了一個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. GC 的兩種判定方法

  • 引用計數法:指的是如果某個地方引用了這個對象就+1,如果失效了就-1,當為 0 就會回收但是 JVM沒有用這種方式,因為無法判定互相循環引用(A 引用 B,B 引用 A) 的情況。
  • 引用鍊法: 通過一種 GC ROOT 的對象(方法區中靜态變量引用的對象等-static 變量)來判斷,如果有一條鍊能夠到達 GCROOT 就說明,不能到達 GC ROOT 就說明可以回收。

4. Minor GC 與 Full GC 分别在什麼時候發生?

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

5. 類加載的幾個過程:

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

通過全限定名來加載生成 class 對象到記憶體中,然後進行驗證這個 class 檔案,包括檔案格式校驗、中繼資料驗證,位元組碼校驗等。準備是對這個對象配置設定記憶體。解析是将符号引用轉化為直接引用(指針引用),初始化就是開始執行構造器的代碼

1

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

java 虛拟機主要分為以下幾個區:

方法區:

  • 有時候也成為永久代,在該區内很少發生垃圾回收,但是并不代表不發生 GC,在這裡進行的 GC 主要是對方法區裡的常量池和對類型的解除安裝
  • 方法區主要用來存儲已被虛拟機加載的類的資訊、常量、靜态變量和即時編譯器編譯後的代碼等資料。
  • 該區域是被線程共享的。
  • 方法區裡有一個運作時常量池,用于存放靜态編譯産生的字面量和符号引用。該常量池具有動态性,也就是說常量并不一定是編譯時确定,運作時生成的常量也會存在這個常量池中。

虛拟機棧:

  • 虛拟機棧也就是我們平常所稱的棧記憶體,它為 java方法服務,每個方法在執行的時候都會建立一個棧幀,用于存儲局部變量表、操作數棧、動态連結和方法出口等資訊。
  • 虛拟機棧是線程私有的,它的生命周期與線程相同。
  • 局部變量表裡存儲的是基本資料類型、returnAddress類型(指向一條位元組碼指令的位址)和對象引用,這個對象引用有可能是指向對象起始位址的一個指針,也有可能是代表對象的句柄或者與對象相關聯的位置。局部變量所需的記憶體空間在編譯器間确定
  • 操作數棧的作用主要用來存儲運算結果以及運算的操作數,它不同于局部變量表通過索引來通路,而是壓棧和出棧的方式
  • 每個棧幀都包含一個指向運作時常量池中該棧幀所屬方法的引用,持有這個引用是為了支援方法調用過程中的動态連接配接.動态連結就是将常量池中的符号引用在運作期轉化為直接引用。

本地方法棧:

本地方法棧和虛拟機棧類似,隻不過本地方法棧為 Native 方法服務。

java 堆是所有線程所共享的一塊記憶體,在虛拟機啟動時建立,幾乎所有的對象執行個體都在這裡建立,是以該區域經常發生垃圾回收操作。

程式計數器

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

7.如和判斷一個對象是否存活?(或者 GC 對象的判定方法)

判斷一個對象是否存活有兩種方法:

1.引用計數法

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

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

2.可達性算法(引用鍊法)

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

在 java 中可以作為 GC Roots 的對象有以下幾種:

  • 虛拟機棧中引用的對象
  • 方法區類靜态屬性引用的對象
  • 方法區常量池引用的對象
  • 本地方法棧 JNI 引用的對象

雖然這些算法可以判定一個對象是否能被回收,但是當滿足上述條件時,一個對象比不一定會被回收。當一個對象不可達 GC Root 時,這個對象并不會立馬被回收,而是出于一個死緩的階段,若要被真正的回收需要經曆兩次标記

如果對象在可達性分析中沒有與 GC Root 的引用鍊,那麼此時就會被第一次标記并且進行一次篩選,篩選的條件是是否有必要執行 finalize()方法。當對象沒有覆寫 finalize()方法或者已被虛拟機調用過,那麼就認為是沒必要的。

如果該對象有必要執行 finalize()方法,那麼這個對象将會放在一個稱為 F-Queue 的對隊列中,虛拟機會觸發一個 Finalize()線程去執行,此線程是低優先級的,并且虛拟機不會承

諾一直等待它運作完,這是因為如果 finalize()執行緩慢或者發生了死鎖,那麼就會造成 FQueue 隊列一直等待,造成了記憶體回收系統的崩潰。GC 對處于 F-Queue 中的對象進行第二次被标記,這時,該對象将被移除”即将回收”集合,等待回收。

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

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

1.效率不高,标記和清除的效率都很低;

2.會産生大量不連續的記憶體碎片,導緻以後程式在配置設定較大的對象時,由于沒有充足的連續記憶體而提前觸發一次 GC 動作。

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

于是将該算法進行了改進,記憶體區域不再是按照 1:1 去劃分,而是将記憶體劃分為8:1:1 三部分,較大那份記憶體交 Eden 區,其餘是兩塊較小的記憶體區叫 Survior 區。

每次都會優先使用 Eden 區,若 Eden 區滿,就将對象複制到第二塊記憶體區上,然後清除 Eden 區,如果此時存活的對象太多,以至于 Survivor 不夠時,會将這些對象通過配置設定擔保機制複制到老年代中。(java 堆又分為新生代和老年代)

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

分代收集:現在的虛拟機垃圾收集大多采用這種方式,它根據對象的生存周期,将堆分為新生代和老年代。在新生代中,由于對象生存期短,每次回收都會有大量對象死去,那麼這時就采用複制算法。老年代裡的對象存活率較高,沒有額外的空間進行配置設定擔保,是以可以使用标記-整理或者 标記-清除。

9.什麼是類加載器,類加載器有哪些?

實作通過類的權限定名擷取該類的二進制位元組流的代碼塊叫做類加載器。

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

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

10. 類加載器雙親委派模型機制?

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

11.什麼情況下會發生棧記憶體溢出?

1、棧是線程私有的,棧的生命周期和線程一樣,每個方法在執行的時候就會建立一個棧幀,它包含局部變量表、操作數棧、動态連結、方法出口等資訊,局部變量表又包括基本資料類型和對象的引用;

2、當線程請求的棧深度超過了虛拟機允許的最大深度時,會抛出StackOverFlowError異常,方法遞歸調用肯可能會出現該問題;

3、調整參數-xss去調整jvm棧的大小

12.怎麼打破雙親委派模型?

自定義類加載器,繼承ClassLoader類,重寫loadClass方法和findClass方法;

13.強引用、軟應用、弱引用、虛引用的差別?

強引用:強引用是我們使用最廣泛的引用,如果一個對象具有強引用,那麼垃圾回收期絕對不會回收它,當記憶體空間不足時,垃圾回收器甯願抛出OutOfMemoryError,也不會回收具有強引用的對象;我們可以通過顯示的将強引用對象置為null,讓gc認為該對象不存在引用,進而來回收它;

軟引用:軟應用是用來描述一些有用但不是必須的對象,在java中用SoftReference來表示,當一個對象隻有軟應用時,隻有當記憶體不足時,才會回收它;

軟引用可以和引用隊列聯合使用,如果軟引用所引用的對象被垃圾回收器所回收了,虛拟機會把這個軟引用加入到與之對應的引用隊列中;

弱引用:弱引用是用來描述一些可有可無的對象,在java中用WeakReference來表示,在垃圾回收時,一旦發現一個對象隻具有軟引用的時候,無論目前記憶體空間是否充足,都會回收掉該對象;

弱引用可以和引用隊列聯合使用,如果弱引用所引用的對象被垃圾回收了,虛拟機會将該對象的引用加入到與之關聯的引用隊列中;

虛引用:虛引用就是一種可有可無的引用,無法用來表示對象的生命周期,任何時候都可能被回收,虛引用主要使用來跟蹤對象被垃圾回收的活動,虛引用和軟引用與弱引用的差別在于:虛引用必須和引用隊列聯合使用;在進行垃圾回收的時候,如果發現一個對象隻有虛引用,那麼就會将這個對象的引用加入到與之關聯的引用隊列中,程式可以通過發現一個引用隊列中是否已經加入了虛引用,來了解被引用的對象是否需要被進行垃圾回收;

總結:

JVM在一些網際網路大廠是面試必問的一個技術點,是以在面試時一定要注重重點,想一些高并發高可用的技術。面試時要掌握節奏,說一些讓面試官眼前一亮的技術,有些基礎的東西能少說就少說,畢竟面試官面了這麼多早就聽夠了,越是稀少的越是能激發面試官的興趣,然後掌握在自己的節奏中。

另外本人整理收藏了20年多家公司面試知識點整理 ,以及各種Java核心知識點免費分享給大家,想要資料的話請點1065653031暗号CSDN。

面試官:我就問了一個JVM,沒想到他能吹半個小時
面試官:我就問了一個JVM,沒想到他能吹半個小時