天天看點

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了
記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

來源:https://thinkwon.blog.csdn.net/article/details/104390752

文章目錄

    • 說一下 JVM 調優的工具?
    • 常用的 JVM 調優的參數都有哪些?
    • 簡述java類加載機制?
    • 描述一下JVM加載Class檔案的原理機制
    • 什麼是類加載器,類加載器有哪些?
    • 說一下類裝載的執行過程?
    • 什麼是雙親委派模型?
    • 簡述java記憶體配置設定與回收策率以及Minor GC和Major GC
    • 對象優先在 Eden 區配置設定
    • 大對象直接進入老年代
    • 長期存活對象将進入老年代
    • 簡述Java垃圾回收機制
    • GC是什麼?為什麼要GC
    • 垃圾回收的優點和原理。并考慮2種回收機制
    • 垃圾回收器的基本原理是什麼?垃圾回收器可以馬上回收記憶體嗎?有什麼辦法主動通知虛拟機進行垃圾回收?
    • Java 中都有哪些引用類型?
    • 怎麼判斷對象是否可以被回收?
    • 在Java中,對象什麼時候可以被垃圾回收
    • JVM中的永久代中會發生垃圾回收嗎
    • 說一下 JVM 有哪些垃圾回收算法?
    • 說一下 JVM 有哪些垃圾回收器?
    • 詳細介紹一下 CMS 垃圾回收器?
    • 新生代垃圾回收器和老年代垃圾回收器都有哪些?有什麼差別?
    • 簡述分代垃圾回收器是怎麼工作的?
    • 标記-清除算法
    • 複制算法
    • 标記-整理算法
    • 分代收集算法
    • Java會存在記憶體洩漏嗎?請簡單描述
    • 對象的建立
    • 為對象配置設定記憶體
    • 處理并發安全問題
    • 對象的通路定位
    • 句柄通路
    • 直接指針
    • 說一下 JVM 的主要組成部分及其作用?
    • 說一下 JVM 運作時資料區
    • 深拷貝和淺拷貝
    • 說一下堆棧的差別?
    • 隊列和棧是什麼?有什麼差別?
    • Java記憶體區域
    • HotSpot虛拟機對象探秘
    • 記憶體溢出異常
    • 垃圾收集器
    • 記憶體配置設定政策
    • 虛拟機類加載機制
    • JVM調優

Java記憶體區域

說一下 JVM 的主要組成部分及其作用?

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

JVM包含兩個子系統和兩個元件,兩個子系統為Class loader(類裝載)、Execution engine(執行引擎);兩個元件為Runtime data area(運作時資料區)、Native Interface(本地接口)。

  • Class loader(類裝載):根據給定的全限定名類名(如:java.lang.Object)來裝載class檔案到Runtime data area中的method area。
  • Execution engine(執行引擎):執行classes中的指令。
  • Native Interface(本地接口):與native libraries互動,是其它程式設計語言互動的接口。
  • Runtime data area(運作時資料區域):這就是我們常說的JVM的記憶體。

作用 :首先通過編譯器把 Java 代碼轉換成位元組碼,類加載器(ClassLoader)再把位元組碼加載到記憶體中,将其放在運作時資料區(Runtime data area)的方法區内,而位元組碼檔案隻是 JVM 的一套指令集規範,并不能直接交給底層作業系統去執行,是以需要特定的指令解析器執行引擎(Execution Engine),将位元組碼翻譯成底層系統指令,再交由 CPU 去執行,而這個過程中需要調用其他語言的本地庫接口(Native Interface)來實作整個程式的功能。

下面是Java程式運作機制詳細說明

Java程式運作機制步驟

  • 首先利用IDE內建開發工具編寫Java源代碼,源檔案的字尾為.java;
  • 再利用編譯器(javac指令)将源代碼編譯成位元組碼檔案,位元組碼檔案的字尾名為.class;
  • 運作位元組碼的工作是由解釋器(java指令)來完成的。
記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

從上圖可以看,java檔案通過編譯器變成了.class檔案,接下來類加載器又将這些.class檔案加載到JVM中。

其實可以一句話來解釋:類的加載指的是将類的.class檔案中的二進制資料讀入到記憶體中,将其放在運作時資料區的方法區内,然後在堆區建立一個 java.lang.Class對象,用來封裝類在方法區内的資料結構。

說一下 JVM 運作時資料區

Java 虛拟機在執行 Java 程式的過程中會把它所管理的記憶體區域劃分為若幹個不同的資料區域。這些區域都有各自的用途,以及建立和銷毀的時間,有些區域随着虛拟機程序的啟動而存在,有些區域則是依賴線程的啟動和結束而建立和銷毀。Java 虛拟機所管理的記憶體被劃分為如下幾個區域:

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

不同虛拟機的運作時資料區可能略微有所不同,但都會遵從 Java 虛拟機規範, Java 虛拟機規範規定的區域分為以下 5 個部分:

  • 程式計數器(Program Counter Register):目前線程所執行的位元組碼的行号訓示器,位元組碼解析器的工作是通過改變這個計數器的值,來選取下一條需要執行的位元組碼指令,分支、循環、跳轉、異常處理、線程恢複等基礎功能,都需要依賴這個計數器來完成;
  • Java 虛拟機棧(Java Virtual Machine Stacks):用于存儲局部變量表、操作數棧、動态連結、方法出口等資訊;
  • 本地方法棧(Native Method Stack):與虛拟機棧的作用是一樣的,隻不過虛拟機棧是服務 Java 方法的,而本地方法棧是為虛拟機調用 Native 方法服務的;
  • Java 堆(Java Heap):Java 虛拟機中記憶體最大的一塊,是被所有線程共享的,幾乎所有的對象執行個體都在這裡配置設定記憶體;
  • 方法區(Methed Area):用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯後的代碼等資料。

深拷貝和淺拷貝

淺拷貝(shallowCopy)隻是增加了一個指針指向已存在的記憶體位址,

深拷貝(deepCopy)是增加了一個指針并且申請了一個新的記憶體,使這個增加的指針指向這個新的記憶體,

使用深拷貝的情況下,釋放記憶體的時候不會因為出現淺拷貝時釋放同一個記憶體的錯誤。

淺複制:僅僅是指向被複制的記憶體位址,如果原位址發生改變,那麼淺複制出來的對象也會相應的改變。

深複制:在計算機中開辟一塊新的記憶體位址用于存放複制的對象。

說一下堆棧的差別?

實體位址

堆的實體位址配置設定對對象是不連續的。是以性能慢些。在GC的時候也要考慮到不連續的配置設定,是以有各種算法。比如,标記-消除,複制,标記-壓縮,分代(即新生代使用複制算法,老年代使用标記——壓縮)

棧使用的是資料結構中的棧,先進後出的原則,實體位址配置設定是連續的。是以性能快。

記憶體分别

堆因為是不連續的,是以配置設定的記憶體是在

運作期

确認的,是以大小不固定。一般堆大小遠遠大于棧。

棧是連續的,是以配置設定的記憶體大小要在

編譯期

就确認,大小是固定的。

存放的内容

堆存放的是對象的執行個體和數組。是以該區更關注的是資料的存儲

棧存放:局部變量,操作數棧,傳回結果。該區更關注的是程式方法的執行。

PS:

  1. 靜态變量放在方法區
  2. 靜态的對象還是放在堆。

程式的可見度

堆對于整個應用程式都是共享、可見的。

棧隻對于線程是可見的。是以也是線程私有。他的生命周期和線程相同。

隊列和棧是什麼?有什麼差別?

隊列和棧都是被用來預存儲資料的。

  • 操作的名稱不同。隊列的插入稱為入隊,隊列的删除稱為出隊。棧的插入稱為進棧,棧的删除稱為出棧。
  • 可操作的方式不同。隊列是在隊尾入隊,隊頭出隊,即兩邊都可操作。而棧的進棧和出棧都是在棧頂進行的,無法對棧底直接進行操作。
  • 操作的方法不同。隊列是先進先出(FIFO),即隊列的修改是依先進先出的原則進行的。新來的成員總是加入隊尾(不能從中間插入),每次離開的成員總是隊列頭上(不允許中途離隊)。而棧為後進先出(LIFO),即每次删除(出棧)的總是目前棧中最新的元素,即最後插入(進棧)的元素,而最先插入的被放在棧的底部,要到最後才能删除。

HotSpot虛拟機對象探秘

對象的建立

說到對象的建立,首先讓我們看看 

Java

 中提供的幾種對象建立方式:

Header 解釋
使用new關鍵字 調用了構造函數
使用Class的newInstance方法 調用了構造函數
使用Constructor類的newInstance方法 調用了構造函數
使用clone方法 沒有調用構造函數
使用反序列化 沒有調用構造函數

下面是對象建立的主要流程:

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

虛拟機遇到一條new指令時,先檢查常量池是否已經加載相應的類,如果沒有,必須先執行相應的類加載。類加載通過後,接下來配置設定記憶體。若Java堆中記憶體是絕對規整的,使用“指針碰撞“方式配置設定記憶體;如果不是規整的,就從空閑清單中配置設定,叫做”空閑清單“方式。劃分記憶體時還需要考慮一個問題-并發,也有兩種方式: CAS同步處理,或者本地線程配置設定緩沖(Thread Local Allocation Buffer, TLAB)。然後記憶體空間初始化操作,接着是做一些必要的對象設定(元資訊、哈希碼…),最後執行

方法。

為對象配置設定記憶體

類加載完成後,接着會在Java堆中劃分一塊記憶體配置設定給對象。記憶體配置設定根據Java堆是否規整,有兩種方式:

  • 指針碰撞:如果Java堆的記憶體是規整,即所有用過的記憶體放在一邊,而空閑的的放在另一邊。配置設定記憶體時将位于中間的指針訓示器向空閑的記憶體移動一段與對象大小相等的距離,這樣便完成配置設定記憶體工作。
  • 空閑清單:如果Java堆的記憶體不是規整的,則需要由虛拟機維護一個清單來記錄那些記憶體是可用的,這樣在配置設定的時候可以從清單中查詢到足夠大的記憶體配置設定給對象,并在配置設定後更新清單記錄。

選擇哪種配置設定方式是由 Java 堆是否規整來決定的,而 Java 堆是否規整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

處理并發安全問題

對象的建立在虛拟機中是一個非常頻繁的行為,哪怕隻是修改一個指針所指向的位置,在并發情況下也是不安全的,可能出現正在給對象 A 配置設定記憶體,指針還沒來得及修改,對象 B 又同時使用了原來的指針來配置設定記憶體的情況。解決這個問題有兩種方案:

  • 對配置設定記憶體空間的動作進行同步處理(采用 CAS + 失敗重試來保障更新操作的原子性);
  • 把記憶體配置設定的動作按照線程劃分在不同的空間之中進行,即每個線程在 Java 堆中預先配置設定一小塊記憶體,稱為本地線程配置設定緩沖(Thread Local Allocation Buffer, TLAB)。哪個線程要配置設定記憶體,就在哪個線程的 TLAB 上配置設定。隻有 TLAB 用完并配置設定新的 TLAB 時,才需要同步鎖。通過-XX:+/-UserTLAB參數來設定虛拟機是否使用TLAB。
記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

對象的通路定位

Java

程式需要通過 

JVM

 棧上的引用通路堆中的具體對象。對象的通路方式取決于 

JVM

 虛拟機的實作。目前主流的通路方式有 句柄 和 直接指針 兩種方式。

指針: 指向對象,代表一個對象在記憶體中的起始位址。

句柄: 可以了解為指向指針的指針,維護着對象的指針。句柄不直接指向對象,而是指向對象的指針(句柄不發生變化,指向固定記憶體位址),再由對象的指針指向對象的真實記憶體位址。

句柄通路

Java

堆中劃分出一塊記憶體來作為句柄池,引用中存儲對象的句柄位址,而句柄中包含了對象執行個體資料與對象類型資料各自的具體位址資訊,具體構造如下圖所示:

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

優勢:引用中存儲的是穩定的句柄位址,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時隻會改變句柄中的執行個體資料指針,而引用本身不需要修改。

直接指針

如果使用直接指針通路,引用 中存儲的直接就是對象位址,那麼

Java

堆對象内部的布局中就必須考慮如何放置通路類型資料的相關資訊。

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

優勢:速度更快,節省了一次指針定位的時間開銷。由于對象的通路在

Java

中非常頻繁,是以這類開銷積少成多後也是非常可觀的執行成本。HotSpot 中采用的就是這種方式。

記憶體溢出異常

Java會存在記憶體洩漏嗎?請簡單描述

記憶體洩漏是指不再被使用的對象或者變量一直被占據在記憶體中。理論上來說,Java是有GC垃圾回收機制的,也就是說,不再被使用的對象,會被GC自動回收掉,自動從記憶體中清除。

但是,即使這樣,Java也還是存在着記憶體洩漏的情況,java導緻記憶體洩露的原因很明确:長生命周期的對象持有短生命周期對象的引用就很可能發生記憶體洩露,盡管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導緻不能被回收,這就是java中記憶體洩露的發生場景。

垃圾收集器

簡述Java垃圾回收機制

在java中,程式員是不需要顯示的去釋放一個對象的記憶體的,而是由虛拟機自行執行。在JVM中,有一個垃圾回收線程,它是低優先級的,在正常情況下是不會執行的,隻有在虛拟機空閑或者目前堆記憶體不足時,才會觸發執行,掃面那些沒有被任何引用的對象,并将它們添加到要回收的集合中,進行回收。

GC是什麼?為什麼要GC

GC 是垃圾收集的意思(Gabage Collection),記憶體處理是程式設計人員容易出現問題的地方,忘記或者錯誤的記憶體

回收會導緻程式或系統的不穩定甚至崩潰,Java 提供的 GC 功能可以自動監測對象是否超過作用域進而達到自動

回收記憶體的目的,Java 語言沒有提供釋放已配置設定記憶體的顯示操作方法。

垃圾回收的優點和原理。并考慮2種回收機制

java語言最顯著的特點就是引入了垃圾回收機制,它使java程式員在編寫程式時不再考慮記憶體管理的問題。

由于有這個垃圾回收機制,java中的對象不再有“作用域”的概念,隻有引用的對象才有“作用域”。

垃圾回收機制有效的防止了記憶體洩露,可以有效的使用可使用的記憶體。

垃圾回收器通常作為一個單獨的低級别的線程運作,在不可預知的情況下對記憶體堆中已經死亡的或很長時間沒有用過的對象進行清除和回收。

程式員不能實時的對某個對象或所有對象調用垃圾回收器進行垃圾回收。

垃圾回收有分代複制垃圾回收、标記垃圾回收、增量垃圾回收。

垃圾回收器的基本原理是什麼?垃圾回收器可以馬上回收記憶體嗎?有什麼辦法主動通知虛拟機進行垃圾回收?

對于GC來說,當程式員建立對象時,GC就開始監控這個對象的位址、大小以及使用情況。

通常,GC采用有向圖的方式記錄和管理堆(heap)中的所有對象。通過這種方式确定哪些對象是"可達的",哪些對象是"不可達的"。當GC确定一些對象為"不可達"時,GC就有責任回收這些記憶體空間。

可以。程式員可以手動執行System.gc(),通知GC運作,但是Java語言規範并不保證GC一定會執行。

Java 中都有哪些引用類型?

  • 強引用:發生 gc 的時候不會被回收。
  • 軟引用:有用但不是必須的對象,在發生記憶體溢出之前會被回收。
  • 弱引用:有用但不是必須的對象,在下一次GC時會被回收。
  • 虛引用(幽靈引用/幻影引用):無法通過虛引用獲得對象,用 PhantomReference 實作虛引用,虛引用的用途是在 gc 時傳回一個通知。

怎麼判斷對象是否可以被回收?

垃圾收集器在做垃圾回收的時候,首先需要判定的就是哪些記憶體是需要被回收的,哪些對象是「存活」的,是不可以被回收的;哪些對象已經「死掉」了,需要被回收。

一般有兩種方法來判斷:

  • 引用計數器法:為每個對象建立一個引用計數,有對象引用時計數器 +1,引用被釋放時計數 -1,當計數器為 0 時就可以被回收。它有一個缺點不能解決循環引用的問題;
  • 可達性分析算法:從 GC Roots 開始向下搜尋,搜尋所走過的路徑稱為引用鍊。當一個對象到 GC Roots 沒有任何引用鍊相連時,則證明此對象是可以被回收的。

在Java中,對象什麼時候可以被垃圾回收

當對象對目前使用這個對象的應用程式變得不可觸及的時候,這個對象就可以被回收了。

垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收(Full GC)。如果你仔細檢視垃圾收集器的輸出資訊,就會發現永久代也是被回收的。這就是為什麼正确的永久代大小對避免Full GC是非常重要的原因。

JVM中的永久代中會發生垃圾回收嗎

垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收(Full GC)。如果你仔細檢視垃圾收集器的輸出資訊,就會發現永久代也是被回收的。這就是為什麼正确的永久代大小對避免Full GC是非常重要的原因。請參考下Java8:從永久代到中繼資料區

(譯者注:Java8中已經移除了永久代,新加了一個叫做中繼資料區的native記憶體區)

說一下 JVM 有哪些垃圾回收算法?

  • 标記-清除算法:标記無用對象,然後進行清除回收。缺點:效率不高,無法清除垃圾碎片。
  • 複制算法:按照容量劃分二個大小相等的記憶體區域,當一塊用完的時候将活着的對象複制到另一塊上,然後再把已使用的記憶體空間一次清理掉。缺點:記憶體使用率不高,隻有原來的一半。
  • 标記-整理算法:标記無用對象,讓所有存活的對象都向一端移動,然後直接清除掉端邊界以外的記憶體。
  • 分代算法:根據對象存活周期的不同将記憶體劃分為幾塊,一般是新生代和老年代,新生代基本采用複制算法,老年代采用标記整理算法。

标記-清除算法

标記無用對象,然後進行清除回收。

标記-清除算法(Mark-Sweep)是一種常見的基礎垃圾收集算法,它将垃圾收集分為兩個階段:

  • 标記階段:标記出可以回收的對象。
  • 清除階段:回收被标記的對象所占用的空間。

标記-清除算法之是以是基礎的,是因為後面講到的垃圾收集算法都是在此算法的基礎上進行改進的。

優點:實作簡單,不需要對象進行移動。

缺點:标記、清除過程效率低,産生大量不連續的記憶體碎片,提高了垃圾回收的頻率。

标記-清除算法的執行的過程如下圖所示

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

複制算法

為了解決标記-清除算法的效率不高的問題,産生了複制算法。它把記憶體空間劃為兩個相等的區域,每次隻使用其中一個區域。垃圾收集時,周遊目前使用的區域,把存活對象複制到另外一個區域中,最後将目前使用的區域的可回收的對象進行回收。

優點:按順序配置設定記憶體即可,實作簡單、運作高效,不用考慮記憶體碎片。

缺點:可用的記憶體大小縮小為原來的一半,對象存活率高時會頻繁進行複制。

複制算法的執行過程如下圖所示

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

标記-整理算法

在新生代中可以使用複制算法,但是在老年代就不能選擇複制算法了,因為老年代的對象存活率會較高,這樣會有較多的複制操作,導緻效率變低。标記-清除算法可以應用在老年代中,但是它效率不高,在記憶體回收後容易産生大量記憶體碎片。是以就出現了一種标記-整理算法(Mark-Compact)算法,與标記-整理算法不同的是,在标記可回收的對象後将所有存活的對象壓縮到記憶體的一端,使他們緊湊的排列在一起,然後對端邊界以外的記憶體進行回收。回收後,已用和未用的記憶體都各自一邊。

優點:解決了标記-清理算法存在的記憶體碎片問題。

缺點:仍需要進行局部對象移動,一定程度上降低了效率。

标記-整理算法的執行過程如下圖所示

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

分代收集算法

目前商業虛拟機都采用分代收集的垃圾收集算法。分代收集算法,顧名思義是根據對象的存活周期将記憶體劃分為幾塊。一般包括年輕代、老年代 和 永久代,如圖所示:

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

說一下 JVM 有哪些垃圾回收器?

如果說垃圾收集算法是記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的具體實作。下圖展示了7種作用于不同分代的收集器,其中用于回收新生代的收集器包括Serial、PraNew、Parallel Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS,還有用于回收整個Java堆的G1收集器。不同收集器之間的連線表示它們可以搭配使用。

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了
  • Serial收集器(複制算法): 新生代單線程收集器,标記和清理都是單線程,優點是簡單高效;
  • ParNew收集器 (複制算法): 新生代收并行集器,實際上是Serial收集器的多線程版本,在多核CPU環境下有着比Serial更好的表現;
  • Parallel Scavenge收集器 (複制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 使用者線程時間/(使用者線程時間+GC線程時間),高吞吐量可以高效率的利用CPU時間,盡快完成程式的運算任務,适合背景應用等對互動相應要求不高的場景;
  • Serial Old收集器 (标記-整理算法): 老年代單線程收集器,Serial收集器的老年代版本;
  • Parallel Old收集器 (标記-整理算法):老年代并行收集器,吞吐量優先,Parallel Scavenge收集器的老年代版本;
  • CMS(Concurrent Mark Sweep)收集器(标記-清除算法):老年代并行收集器,以擷取最短回收停頓時間為目标的收集器,具有高并發、低停頓的特點,追求最短GC回收停頓時間。
  • G1(Garbage First)收集器 (标記-整理算法):Java堆并行收集器,G1收集器是JDK1.7提供的一個新收集器,G1收集器基于“标記-整理”算法實作,也就是說不會産生記憶體碎片。此外,G1收集器不同于之前的收集器的一個重要特點是:G1回收的範圍是整個Java堆(包括新生代,老年代),而前六種收集器回收的範圍僅限于新生代或老年代。

詳細介紹一下 CMS 垃圾回收器?

CMS 是英文 Concurrent Mark-Sweep 的簡稱,是以犧牲吞吐量為代價來獲得最短回收停頓時間的垃圾回收器。對于要求伺服器響應速度的應用上,這種垃圾回收器非常适合。在啟動 JVM 的參數加上“-XX:+UseConcMarkSweepGC”來指定使用 CMS 垃圾回收器。

CMS 使用的是标記-清除的算法實作的,是以在 gc 的時候回産生大量的記憶體碎片,當剩餘記憶體不能滿足程式運作要求時,系統将會出現 Concurrent Mode Failure,臨時 CMS 會采用 Serial Old 回收器進行垃圾清除,此時的性能将會被降低。

新生代垃圾回收器和老年代垃圾回收器都有哪些?有什麼差別?

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

新生代垃圾回收器一般采用的是複制算法,複制算法的優點是效率高,缺點是記憶體使用率低;老年代回收器一般采用的是标記-整理的算法進行垃圾回收。

簡述分代垃圾回收器是怎麼工作的?

分代回收器有兩個分區:老生代和新生代,新生代預設的空間占比總空間的 1/3,老生代的預設占比是 2/3。

新生代使用的是複制算法,新生代裡有 3 個分區:Eden、To Survivor、From Survivor,它們的預設占比是 8:1:1,它的執行流程如下:

  • 把 Eden + From Survivor 存活的對象放入 To Survivor 區;
  • 清空 Eden 和 From Survivor 分區;
  • From Survivor 和 To Survivor 分區交換,From Survivor 變 To Survivor,To Survivor 變 From Survivor。

每次在 From Survivor 到 To Survivor 移動時都存活的對象,年齡就 +1,當年齡到達 15(預設配置是 15)時,更新為老生代。大對象也會直接進入老生代。

老生代當空間占用到達某個值之後就會觸發全局垃圾收回,一般使用标記整理的執行算法。以上這些循環往複就構成了整個分代垃圾回收的整體執行流程。

記憶體配置設定政策

簡述java記憶體配置設定與回收策率以及Minor GC和Major GC

所謂自動記憶體管理,最終要解決的也就是記憶體配置設定和記憶體回收兩個問題。前面我們介紹了記憶體回收,這裡我們再來聊聊記憶體配置設定。

對象的記憶體配置設定通常是在 Java 堆上配置設定(随着虛拟機優化技術的誕生,某些場景下也會在棧上配置設定,後面會詳細介紹),對象主要配置設定在新生代的 Eden 區,如果啟動了本地線程緩沖,将按照線程優先在 TLAB 上配置設定。少數情況下也會直接在老年代上配置設定。總的來說配置設定規則不是百分百固定的,其細節取決于哪一種垃圾收集器組合以及虛拟機相關參數有關,但是虛拟機對于記憶體的配置設定還是會遵循以下幾種「普世」規則:

對象優先在 Eden 區配置設定

多數情況,對象都在新生代 Eden 區配置設定。當 Eden 區配置設定沒有足夠的空間進行配置設定時,虛拟機将會發起一次 Minor GC。如果本次 GC 後還是沒有足夠的空間,則将啟用配置設定擔保機制在老年代中配置設定記憶體。

這裡我們提到 Minor GC,如果你仔細觀察過 GC 日常,通常我們還能從日志中發現 Major GC/Full GC。

  • Minor GC 是指發生在新生代的 GC,因為 Java 對象大多都是朝生夕死,所有 Minor GC 非常頻繁,一般回收速度也非常快;
  • Major GC/Full GC 是指發生在老年代的 GC,出現了 Major GC 通常會伴随至少一次 Minor GC。Major GC 的速度通常會比 Minor GC 慢 10 倍以上。

大對象直接進入老年代

所謂大對象是指需要大量連續記憶體空間的對象,頻繁出現大對象是緻命的,會導緻在記憶體還有不少空間的情況下提前觸發 GC 以擷取足夠的連續空間來安置新對象。

前面我們介紹過新生代使用的是标記-清除算法來處理垃圾回收的,如果大對象直接在新生代配置設定就會導緻 Eden 區和兩個 Survivor 區之間發生大量的記憶體複制。是以對于大對象都會直接在老年代進行配置設定。

長期存活對象将進入老年代

虛拟機采用分代收集的思想來管理記憶體,那麼記憶體回收時就必須判斷哪些對象應該放在新生代,哪些對象應該放在老年代。是以虛拟機給每個對象定義了一個對象年齡的計數器,如果對象在 Eden 區出生,并且能夠被 Survivor 容納,将被移動到 Survivor 空間中,這時設定對象年齡為 1。對象在 Survivor 區中每「熬過」一次 Minor GC 年齡就加 1,當年齡達到一定程度(預設 15) 就會被晉升到老年代。

虛拟機類加載機制

簡述java類加載機制?

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

描述一下JVM加載Class檔案的原理機制

Java中的所有類,都需要由類加載器裝載到JVM中才能運作。類加載器本身也是一個類,而它的工作就是把class檔案從硬碟讀取到記憶體中。在寫程式的時候,我們幾乎不需要關心類的加載,因為這些都是隐式裝載的,除非我們有特殊的用法,像是反射,就需要顯式的加載所需要的類。

類裝載方式,有兩種 :

1.隐式裝載, 程式在運作過程中當碰到通過new 等方式生成對象時,隐式調用類裝載器加載對應的類到jvm中,

2.顯式裝載, 通過class.forname()等方法,顯式加載需要的類

Java類的加載是動态的,它并不會一次性将所有類全部加載後再運作,而是保證程式運作的基礎類(像是基類)完全加載到jvm中,至于其他類,則在需要的時候才加載。這當然就是為了節省記憶體開銷。

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

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

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

  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類的方式實作。

說一下類裝載的執行過程?

類裝載分為以下 5 個步驟:

  • 加載:根據查找路徑找到相應的 class 檔案然後導入;
  • 驗證:檢查加載的 class 檔案的正确性;
  • 準備:給類中的靜态變量配置設定記憶體空間;
  • 解析:虛拟機将常量池中的符号引用替換成直接引用的過程。符号引用就了解為一個标示,而在直接引用直接指向記憶體中的位址;
  • 初始化:對靜态變量和靜态代碼塊執行初始化工作。

什麼是雙親委派模型?

在介紹雙親委派模型之前先說下類加載器。對于任意一個類,都需要由加載它的類加載器和這個類本身一同确立在 JVM 中的唯一性,每一個類加載器,都有一個獨立的類名稱空間。類加載器就是根據指定全限定名稱将 class 檔案加載到 JVM 記憶體,然後再轉化為 class 對象。

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

類加載器分類:

  • 啟動類加載器(Bootstrap ClassLoader),是虛拟機自身的一部分,用來加載Java_HOME/lib/目錄中的,或者被 -Xbootclasspath 參數所指定的路徑中并且被虛拟機識别的類庫;
  • 其他類加載器:
  • 擴充類加載器(Extension ClassLoader):負責加載\lib\ext目錄或Java. ext. dirs系統變量指定的路徑中的所有類庫;
  • 應用程式類加載器(Application ClassLoader)。負責加載使用者類路徑(classpath)上的指定類庫,我們可以直接使用這個類加載器。一般情況,如果我們沒有自定義類加載器預設就是用這個加載器。

雙親委派模型:如果一個類加載器收到了類加載的請求,它首先不會自己去加載這個類,而是把這個請求委派給父類加載器去完成,每一層的類加載器都是如此,這樣所有的加載請求都會被傳送到頂層的啟動類加載器中,隻有當父加載無法完成加載請求(它的搜尋範圍中沒找到所需的類)時,子加載器才會嘗試去加載類。

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

JVM調優

說一下 JVM 調優的工具?

JDK 自帶了很多監控工具,都位于 JDK 的 bin 目錄下,其中最常用的是 jconsole 和 jvisualvm 這兩款視圖監控工具。

  • jconsole:用于對 JVM 中的記憶體、線程和類等進行監控;
  • jvisualvm:JDK 自帶的全能分析工具,可以分析:記憶體快照、線程快照、程式死鎖、監控記憶體的變化、gc 變化等。

常用的 JVM 調優的參數都有哪些?

  • -Xms2g:初始化推大小為 2g;
  • -Xmx2g:堆最大記憶體為 2g;
  • -XX:NewRatio=4:設定年輕的和老年代的記憶體比例為 1:4;
  • -XX:SurvivorRatio=8:設定新生代 Eden 和 Survivor 比例為 8:2;
  • –XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器組合;
  • -XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器組合;
  • -XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器組合;
  • -XX:+PrintGC:開啟列印 gc 資訊;
  • -XX:+PrintGCDetails:列印 gc 詳細資訊。

(END)

點這裡在看

記憶體配置設定算法java_Java虛拟機(JVM面試),看這篇就夠了

繼續閱讀