Java筆試面試-JVM

1.什麼是 JVM?它有什麼作用?

答:JVM 是 Java Virtual Machine(Java 虛擬機)的縮寫,顧名思義它是一個虛擬計算機,也是 Java 程序能夠實現跨平臺的基礎。它的作用是加載 Java 程序,把字節碼翻譯成機器碼再交由 CPU 執行的一個虛擬計算器。

2.JVM 主要組成部分有哪些?

答:JVM 主要組成部分如下:

  • 類加載器(ClassLoader)
  • 運行時數據區(Runtime Data Area)
  • 執行引擎(Execution Engine)
  • 本地庫接口(Native Interface)

3.JVM 是如何工作的?

答:首先程序在執行之前先要把 Java 代碼(.java)轉換成字節碼(.class),JVM 通過類加載器(ClassLoader)把字節碼加載到內存中,但字節碼文件是 JVM 的一套指令集規範,並不能直接交給底層操作系統去執行,因此需要特定的命令解析器執行引擎(Execution Engine) 將字節碼翻譯成底層機器碼,再交由 CPU 去執行,CPU 執行的過程中需要調用本地庫接口(Native Interface)來完成整個程序的運行。

4.JVM 內存佈局是怎樣的?

答:不同虛擬機實現可能略微有所不同,但都會遵從 Java 虛擬機規範,Java 8 虛擬機規範規定,Java 虛擬機所管理的內存將會包括以下幾個區域:

  • 程序計數器(Program Counter Register)
  • Java 虛擬機棧(Java Virtual Machine Stacks)
  • 本地方法棧(Native Method Stack)
  • Java 堆(Java Heap)
  • 方法區(Methed Area)

① 程序計數器

  程序計數器(Program Counter Register)是一塊較小的內存空間,它可以看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解析器的工作是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。

  由於 JVM 的多線程是通過線程輪流切換並分配處理器執行時間的方式來實現的,也就是任何時刻,一個處理器(或者說一個內核)都只會執行一條線程中的指令。因此爲了線程切換後能恢復到正確的執行位置,每個線程都有獨立的程序計數器。

  如果線程正在執行 Java 中的方法,程序計數器記錄的就是正在執行虛擬機字節碼指令的地址,如果是 Native 方法,這個計數器就爲空(undefined),因此該內存區域是唯一一個在 Java 虛擬機規範中沒有規定 OutOfMemoryError 的區域。

② Java 虛擬機棧

  Java 虛擬機棧(Java Virtual Machine Stacks)描述的是 Java 方法執行的內存模型,每個方法在執行的同時都會創建一個線幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息,每個方法從調用直至執行完成的過程,都對應着一個線幀在虛擬機棧中入棧到出棧的過程。

  如果線程請求的棧深度大於虛擬機所允許的棧深度就會拋出 StackOverflowError 異常。如果虛擬機是可以動態擴展的,如果擴展時無法申請到足夠的內存就會拋出 OutOfMemoryError 異常。

③ 本地方法棧

  本地方法棧(Native Method Stack)與虛擬機棧的作用是一樣的,只不過虛擬機棧是服務 Java 方法的,而本地方法棧是爲虛擬機調用 Native 方法服務的。

  在 Java 虛擬機規範中對於本地方法棧沒有特殊的要求,虛擬機可以自由的實現它,因此在 Sun HotSpot 虛擬機直接把本地方法棧和虛擬機棧合二爲一了。

④ Java 堆

  Java 堆(Java Heap)是 JVM 中內存最大的一塊,是被所有線程共享的,在虛擬機啓動時候創建,Java 堆唯一的目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存,隨着JIT編譯器的發展和逃逸分析技術的逐漸成熟,棧上分配、標量替換優化的技術將會導致一些微妙的變化,所有的對象都分配在堆上漸漸變得不那麼絕對了。

  如果在堆中沒有內存完成實例分配,並且堆不可以再擴展時,將會拋出 OutOfMemoryError。 Java 虛擬機規範規定,Java 堆可以處在物理上不連續的內存空間中,只要邏輯上連續即可,就像我們的磁盤空間一樣。在實現上也可以是固定大小的,也可以是可擴展的,不過當前主流的虛擬機都是可擴展的,通過 -Xmx 和 -Xms 控制。

⑤ 方法區

  方法區(Methed Area)用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯後的代碼等數據。

  很多人把方法區稱作“永久代”(Permanent Generation),本質上兩者並不等價,只是 HotSpot 虛擬機垃圾回收器團隊把 GC 分代收集擴展到了方法區,或者說是用來永久代來實現方法區而已,這樣能省去專門爲方法區編寫內存管理的代碼,但是在 JDK 8 也移除了“永久代”,使用 Native Memory 來實現方法區。

  當方法無法滿足內存分配需求時會拋出 OutOfMemoryError 異常。

5.在 Java 中負責字節碼解釋執行的是?

A:應用服務器
B:垃圾回收器
C:虛擬機
D:編譯器

答:C

6.靜態變量存儲在哪個區?

A:棧區
B:堆區
C:全局區
D:常量區

答:C

題目解析:棧區存放函數的參數值,局部變量的值等;堆區存放的是程序員創建的對象;全局區存放全局變量和靜態變量;常量區存放常量字符串。

7.垃圾回收算法有哪些?

答:垃圾回收算法如下。

  • 引用計數器算法:引用計算器判斷對象是否存活的算法是這樣的,給每一個對象設置一個引用計數器,每當有一個地方引用這個對象的時候,計數器就加 1,與之相反,每當引用失效的時候就減 1。
  • 可達性分析算法:在主流的語言的主流實現中,比如 Java、C#,甚至是古老的 Lisp 都是使用的可達性分析算法來判斷對象是否存活的。這個算法的核心思路就是通過一些列的“GC Roots”對象作爲起始點,從這些對象開始往下搜索,搜索所經過的路徑稱之爲“引用鏈”。當一個對象到 GC Roots 沒有任何引用鏈相連的時候,證明此對象是可以被回收的。
  • 複製算法:複製算法是將內存分爲大小相同的兩塊,當這一塊使用完了,就把當前存活的對象複製到另一塊,然後一次性清空當前區塊。此算法的缺點是隻能利用一半的內存空間。
  • 標記-清除算法:此算法執行分兩階段,第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,把未標記的對象清除。此算法需要暫停整個應用,同時,會產生內存碎片。
  • 標記-整理:此算法結合了“標記-清除”和“複製”兩個算法的優點。分爲兩個階段,第一階段從根節點開始標記所有被引用對象,第二階段遍歷整個堆,把清除未標記對象並且把存活對象“壓縮”到堆的其中一塊,按順序排放。此算法避免了“標記-清除”的碎片問題,同時也避免了“複製”算法的空間問題。

8.哪些對象可以作爲引用鏈的 Root 對象?

答:引用鏈的 Root 對象可以爲以下內容:

  • Java 虛擬機棧中的引用對象;
  • 本地方法棧中 JNI(既一般說的 Native 方法)引用的對象;
  • 方法區中類靜態常量的引用對象;
  • 方法區中常量的引用對象。

9.對象引用關係都有哪些?

答:不管是引用計數法還是可達性分析算法都與對象的“引用”有關,這說明對象的引用決定了對象的生死,對象的引用關係如下。

  • 強引用:在代碼中普遍存在的,類似 Object obj = new Object() 這類引用,只要強引用還在,垃圾收集器永遠不會回收掉被引用的對象。
  • 軟引用:是一種相對強引用弱化一些的引用,可以讓對象豁免一些垃圾收集,只有當JVM 認爲內存不足時,纔會去試圖回收軟引用指向的對象,JVM 會確保在拋出 OutOfMemoryError 之前,清理軟引用指向的對象。
  • 弱引用:非必需對象,但它的強度比軟引用更弱,被弱引用關聯的對象只能生存到下一次垃圾收集發生之前。
  • 虛引用:也稱爲幽靈引用或幻影引用,是最弱的一種引用關係,無法通過虛引用來獲取一個對象實例,爲對象設置虛引用的目的只有一個,就是當着個對象被收集器回收時收到一條系統通知。

10.內存溢出和內存泄漏的區別是什麼?

答:內存溢出和內存泄漏的區別如下:

  • 內存溢出是指程序申請內存時,沒有足夠的內存,就會報錯 OutOfMemory;
  • 內存泄漏是指垃圾對象無法回收,可以使用 Memory Analyzer 等工具排出內存泄漏。

11.垃圾回收的分類都有哪些?

答:垃圾回收的分類如下:

  • 新生代回收器:Serial、ParNew、Parallel Scavenge
  • 老年代回收器:Serial Old、Parallel Old、CMS
  • 整堆回收器:G1

12.分代垃圾回收器的組成部分有哪些?

答:分代垃圾回收器是由:新生代(Young Generation)和老生代(Tenured Generation)組成的,默認情況下新生代和老生代的內存比例是 1:2。

13.新生代的組成部分有哪些?

答:新生代是由:Eden、Form Survivor、To Survivor 三個區域組成的,它們內存默認佔比是 8:1:1。

14.新生代垃圾回收是怎麼執行的?

答:新生代垃圾回收的執行過程如下:

① Eden 區 + From Survivor 區存活着的對象複製到 To Survivor 區;
② 清空 Eden 和 From Survivor 分區;
③ From Survivor 和 To Survivor 分區交換(From 變 To,To 變 From)。

15.爲什麼新生代有兩個 Survivor 分區?

答:當新生代的 Survivor 分區爲 2 個的時候,不論是空間利用率還是程序運行的效率都是最優的。

  如果 Survivor 是 0 的話,也就是說新生代只有一個 Eden 分區,每次垃圾回收之後,存活的對象都會進入老生代,這樣老生代的內存空間很快就被佔滿了,從而觸發最耗時的 Full GC ,顯然這樣的收集器的效率是我們完全不能接受的。
  如果 Survivor 分區是 1 個的話,假設把兩個區域分爲 1:1,那麼任何時候都有一半的內存空間是閒置的,顯然空間利用率太低不是最佳的方案。但如果設置內存空間的比例是 8:2 ,只是看起來似乎“很好”,假設新生代的內存爲 100 MB( Survivor 大小爲 20 MB ),現在有 70 MB 對象進行垃圾回收之後,剩餘活躍的對象爲 15 MB 進入 Survivor 區,這個時候新生代可用的內存空間只剩了 5 MB,這樣很快又要進行垃圾回收操作,顯然這種垃圾回收器最大的問題就在於,需要頻繁進行垃圾回收。
  如果 Survivor 分區有 2 個分區,我們就可以把 Eden、From Survivor、To Survivor 分區內存比例設置爲 8:1:1 ,那麼任何時候新生代內存的利用率都 90% ,這樣空間利用率基本是符合預期的。再者就是虛擬機的大部分對象都符合“朝生夕死”的特性,因此每次新對象的產生都在空間佔比比較大的 Eden 區,垃圾回收之後再把存活的對象方法存入 Survivor 區,如果是 Survivor 區存活的對象,那麼“年齡”就 +1 ,當年齡增長到 15 (可通過 -XX:+MaxTenuringThreshold 設定)對象就升級到老生代。
  經過以上對比,可以得出結論,當新生代的 Survivor 分區爲 2 個的時候,不論是空間利用率還是程序運行的效率都是最優的。

16.什麼是 CMS 垃圾回收器?

答:CMS(Concurrent Mark Sweep)一種以獲得最短停頓時間爲目標的收集器,非常適用 B/S 系統。

17.CMS 垃圾回收器有哪些優缺點?

答:CMS 垃圾回收器的優點是使用多線程,標記清除垃圾的,它缺點如下。

  • 對 CPU 資源要求敏感:CMS 回收器過分依賴於多線程環境,默認情況下,開啓的線程數爲(CPU 的數量 + 3)/ 4,當 CPU 數量少於 4 個時,CMS 對用戶本身的操作的影響將會很大,因爲要分出一半的運算能力去執行回收器線程;
  • CMS 無法清除浮動垃圾:浮動垃圾指的是 CMS 清除垃圾的時候,還有用戶線程產生新的垃圾,這部分未被標記的垃圾叫做“浮動垃圾”,只能在下次 GC 的時候進行清除;
  • CMS 垃圾回收會產生大量空間碎片:CMS 使用的是標記-清除算法,所有在垃圾回收的時候回產生大量的空間碎片。

18.什麼是 G1 垃圾回收器?

答:G1 垃圾回收器是一種兼顧吞吐量和停頓時間的 GC 實現,是 JDK 9 以後的默認 GC 選項。G1 可以直觀的設定停頓時間的目標,相比於 CMS CG,G1 未必能做到 CMS 在最好情況下的延時停頓,但是最差情況要好很多。

  G1 GC 仍然存在着年代的概念,但是其內存結構並不是簡單的條帶式劃分,而是類似棋盤的一個個 Region。Region 之間是複製算法,但整體上實際可看作是標記 - 整理(Mark-Compact)算法,可以有效地避免內存碎片,尤其是當 Java 堆非常大的時候,G1 的優勢更加明顯。

19.垃圾回收的調優參數有哪些?

答:垃圾回收的常用調優如下:

  • -Xmx:512 設置最大堆內存爲 512 M;
  • -Xms:215 初始堆內存爲 215 M;
  • -XX:MaxNewSize 設置最大年輕區內存;
  • -XX:MaxTenuringThreshold=5 設置新生代對象經過 5 次 GC 晉升到老年代;
  • -XX:PretrnureSizeThreshold 設置大對象的值,超過這個值的大對象直接進入老生代;
  • -XX:NewRatio 設置分代垃圾回收器新生代和老生代內存佔比;
  • -XX:SurvivorRatio 設置新生代 Eden、Form Survivor、To Survivor 佔比。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章