1. 程式人生 > >JVM面試題目和答案

JVM面試題目和答案

JVM面試題目和答案

JVM記憶體模型

首先我們來了解一下JVM的記憶體模型的怎麼樣的:
在這裡插入圖片描述

1.堆:存放物件例項,幾乎所有的物件例項都在這裡分配記憶體

  • 堆的最小記憶體由-Xms指定,預設是實體記憶體的1/64;最大的記憶體由-Xmx指定,預設是實體記憶體的1/4。
  • 預設空餘的堆記憶體小於40%時,就會增大,直到-Xmx設定的記憶體。具體的比例可以由-XX:MinHeapFreeRatio指定。
  • 空餘的記憶體大於70%時,就會減少記憶體,直到-Xms設定的大小。具體由-XX:MaxHeapFreeRatio指定。

2.虛擬機器棧

虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會同時建立一個棧幀(Stack Frame)用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊本地方法棧:本地方法棧則是為虛擬機器使用到的Native方法服務。

3.方法區:儲存已被虛擬機器載入的類元資料資訊

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

4.程式計數器:當前執行緒所執行的位元組碼的行號指示器

總結:

在這裡插入圖片描述

JVM垃圾回收演算法

  1. 標記-清除: 這是垃圾收集演算法中最基礎的,根據名字就可以知道,它的思想就是標記哪些要被回收的物件,然後統一回收。這種方法很簡單,但是會有兩個主要問題:1.效率不高,標記和清除的效率都很低;2.會產生大量不連續的記憶體碎片,導致以後程式在分配較大的物件時,由於沒有充足的連續記憶體而提前觸發一次GC動作。
  2. 複製演算法: 為了解決效率問題,複製演算法將可用記憶體按容量劃分為相等的兩部分,然後每次只使用其中的一塊,當一塊記憶體用完時,就將還存活的物件複製到第二塊記憶體上,然後一次性清楚完第一塊記憶體,再將第二塊上的物件複製到第一塊。但是這種方式,記憶體的代價太高,每次基本上都要浪費一半的記憶體。 於是將該演算法進行了改進,記憶體區域不再是按照1:1去劃分,而是將記憶體劃分為8:1:1三部分,較大那份記憶體交Eden區,其餘是兩塊較小的記憶體區叫Survior區。每次都會優先使用Eden區,若Eden區滿,就將物件複製到第二塊記憶體區上,然後清除Eden區,如果此時存活的物件太多,以至於Survivor不夠時,會將這些物件通過分配擔保機制複製到老年代中。(java堆又分為新生代和老年代)
  3. 標記-整理 該演算法主要是為了解決標記-清除,產生大量記憶體碎片的問題;當物件存活率較高時,也解決了複製演算法的效率問題。它的不同之處就是在清除物件的時候現將可回收物件移動到一端,然後清除掉端邊界以外的物件,這樣就不會產生記憶體碎片了。
  4. 分代收集 現在的虛擬機器垃圾收集大多采用這種方式,它根據物件的生存週期,將堆分為新生代和老年代。在新生代中,由於物件生存期短,每次回收都會有大量物件死去,那麼這時就採用複製演算法。老年代裡的物件存活率較高,沒有額外的空間進行分配擔保,所以可以使用標記-整理 或者 標記-清除。

JVM垃圾收集器有哪些?以及優劣勢比較?

  1. 序列收集器
    序列收集器是最簡單的,它設計為在單核的環境下工作(32位或者windows),你幾乎不會使用到它。它在工作的時候會暫停整個應用的執行,因此在所有伺服器環境下都不可能被使用。
    使用方法:-XX:+UseSerialGC
  2. 並行收集器
    這是JVM預設的收集器,跟它名字顯示的一樣,它最大的優點是使用多個執行緒來掃描和壓縮堆。缺點是在minor和full GC的時候都會暫停應用的執行。並行收集器最適合用在可以容忍程式停滯的環境使用,它佔用較低的CPU因而能提高應用的吞吐(throughput)。
    使用方法:-XX:+UseParallelGC
    3.CMS收集器
    CMS是Concurrent-Mark-Sweep的縮寫,併發的標記與清除。
    這個演算法使用多個執行緒併發地(concurrent)掃描堆,標記不使用的物件,然後清除它們回收記憶體。在兩種情況下會使應用暫停(Stop the World, STW):
  • 當初次開始標記根物件時initial mark。
  • 當在並行收集時應用又改變了堆的狀態時,需要它從頭再確認一次標記了正確的物件final remark。
    這個收集器最大的問題是在年輕代與老年代收集時會出現的一種競爭情況(race condition),稱為提升失敗promotion failure。物件從年輕代複製到老年代稱為提升promotion,但有時侯老年代需要清理出足夠空間來放這些物件,這需要一定的時間,它收集的速度可能趕不上不斷產生的要提升的年輕代物件的速度,這時就需要做STW的收集。STW正是CMS想避免的問題。為了避免這個問題,需要增加老年代的空間大小或者增加更多的執行緒來做老年代的收集以趕上從年輕代複製物件的速度。
    除了上文所說的內容之外,CMS最大的問題就是記憶體空間碎片化的問題。CMS只有在觸發FullGC的情況下才會對堆空間進行compact。如果線上應用長時間執行,碎片化會非常嚴重,會很容易造成promotion failed。為了解決這個問題線上很多應用通過定期重啟或者手工觸發FullGC來觸發碎片整理。
    對比並行收集器它的一個壞處是需要佔用比較多的CPU。對於大多數長期執行的伺服器應用來說,這通常是值得的,因為它不會導致應用長時間的停滯。但是它不是JVM的預設的收集器。
  1. G1收集器
    如果你的堆記憶體大於4G的話,那麼G1會是要考慮使用的收集器。它是為了更好支援大於4G堆記憶體在JDK 7 u4引入的。G1收集器把堆分成多個區域,大小從1MB到32MB,並使用多個後臺執行緒來掃描這些區域,優先會掃描最多垃圾的區域,這就是它名稱的由來,垃圾優先Garbage First。
    如果在後臺執行緒完成掃描之前堆空間耗光的話,才會進行STW收集。它另外一個優點是它在處理的同時會整理壓縮堆空間,相比CMS只會在完全STW收集的時候才會這麼做。
    使用過大的堆記憶體在過去幾年是存在爭議的,很多開發者從單個JVM分解成使用多個JVM的微服務(micro-service)和基於元件的架構。其他一些因素像分離程式元件、簡化部署和避免重新載入類到記憶體的考慮也促進了這樣的分離。
    除了這些因素,最大的因素當然是避免在STW收集時JVM使用者執行緒停滯時間過長,如果你使用了很大的堆記憶體的話就可能出現這種情況。另外,像Docker那樣的容器技術讓你可以在一臺物理機器上輕鬆部署多個應用也加速了這種趨勢。
    使用方法:-XX:+UseG1GC