深入淺出 Java 中 JVM 記憶體管理
Java崗位面試,JVM是對程式設計師基本功考察,通常會問你對JVM瞭解嗎?
可以分幾部分回答這個問題,首先JVM記憶體劃分 | JVM垃圾回收的含義 | 有哪些GC演算法 以及年輕代和老年代各自特點等等。
1) JVM記憶體劃分:
① 方法區 (執行緒共享) 常量 靜態變數 JIT(即時編譯器)編譯後代碼也在方法區存放
② 堆記憶體(執行緒共享) 垃圾回收的主要場地
③ 程式計數器 當前執行緒執行的位元組碼的位置指示器
④ Java虛擬機器棧(棧記憶體) :儲存區域性變數,基本資料型別以及堆記憶體中物件的引用變數
⑤ 本地方法棧 (C棧):為JVM提供使用native方法的服務
通過這幅圖瞭解一下

JDK 1.8同JDK 1.7 最大的區別是:元資料取代了永久代.元空間的本質和永久代類似,都是對JVM規範中的方法區的實現.其元空間和永久代之間的最大區別在於:元資料空間不在虛擬機器中,而是在本地記憶體中
詳細瞭解一下各個部分
01)程式計數器(PC暫存器)
程式計數器的定義: 程式計數器是一塊較小的記憶體空間,是當前執行緒正在執行的哪一條位元組碼指令的地址,若當前執行緒正在執行的是一個本地方法,那麼此時程式計數器為Undefined
程式計數器的作用:

程式計數器的特點

02)Java虛擬機器棧
定義: 描述Java方法執行過程的記憶體模型
Java虛擬機器棧會為每一個即將執行的Java方法建立一塊叫做"棧幀"的區域,用於存放該方法執行過程中的一些資訊,如 區域性變量表 /運算元棧 /動態連結 /方法出口資訊 .............

壓棧出棧過程:
當方法執行過程中需要建立區域性變數時,就將區域性變數的值存入棧幀的區域性變量表中
Java虛擬機器棧的棧頂是當前正在執行的活動棧,也就是當前正在執行的方法,PC暫存器也會指向這個地址,只有這個活動的棧幀的本地變數可以被運算元棧操作,當前這個棧幀中呼叫另一個方法,與之對應的額棧幀又會被建立,新建立的棧幀壓入棧頂,變成當前的活動棧幀,方法結束後,當前棧幀的返回值變成新的活動棧幀的中的運算元棧的一個運算元,如果沒有返回值,那麼新的活動棧幀中運算元棧的運算元沒有變化
由於Java虛擬機器棧是執行緒對應的,資料不是共享的,因此不用關心資料一致性問題,也不會存在同步鎖的問題
特點

03)本地方法棧(C棧)
定義: 是為了JVM執行native方法準備的空間,由於很多native方法都是用C語言實現的,所以通常又叫C棧,它與Java虛擬機器棧實現的功能類似,只不過本地方法棧描述本地方法執行過程的記憶體模型
棧幀變化過程:
本地方法被執行時,在本地方法棧也會建立一塊棧幀,用於存放該方法的區域性變量表 /運算元棧 /動態連結 /方法出口等資訊; 方法結束後,相應的棧幀也會出棧,並釋放記憶體空間.也會丟擲StackOverFlowError和OutOfMemoryError異常
04) 堆
定義: 堆是用來物件的記憶體空間,幾乎所有的物件都儲存在堆中
特點 :

05)方法區
定義: Java虛擬機器規範中定義方法區是堆的一個邏輯部分,方法區存放以下資訊 已被虛擬機器載入的類資訊 /常量 /靜態變數 /即時編譯後代碼
特點 :

執行時常量池:
方法區中存放:類資訊 常量 靜態變數 即時編譯器變編譯後代碼.常量就存放在執行時常量池中.當類被Java虛擬機器載入後,.class檔案中的常量就存在方法區的執行常量池,而且在執行期間,可以向常量池中新增新的常量,如String類的intern()方法就能在執行期間向常量池中新增字串常量
06) 直接記憶體(堆外記憶體)
直接記憶體是除Java虛擬機器之外的記憶體,但有可能被Java使用
操作直接記憶體:
在NIO中引入了一種基於通道和快取的IO方式,他可以呼叫本地方法的直接分配Java虛擬機器之外的記憶體,然後通過一個儲存在堆中的DirectByteBuffer物件直接操作該記憶體,而無需將外部記憶體中資料複製到堆中再進行操作,從而提高資料操作的效率,直接記憶體的大小不受Java虛擬機器,也會丟擲OutOfMemoryError異常
直接記憶體和堆記憶體比較:

2)類似 -Xms -Xmn這些引數的含義
堆記憶體分配
① : JVM初始分配的記憶體由-Xms指定,預設是實體記憶體的1/64
②: JVM最大分配的記憶體由-Xmx指定,預設是實體記憶體的1/4
③: 預設空餘堆記憶體小於40%時,JVM就會增加堆直到-Xmx的最大限制;空餘堆記憶體大於70%時,JVM會減少堆直到-Xms的最小限制
④: 因此伺服器一般設定-Xms -Xmx相等以避免在每次GC後調整堆大小. 物件的堆記憶體由成為垃圾回收器的自動記憶體管理系統回收
非堆記憶體分配:
①:JVM使用-XX:PermSize 設定非堆記憶體的初始值,預設實體記憶體的1/64;
② :由XX:MaxPermSize設定設定最大非堆記憶體的大小
③: -Xmn2G :設定年輕代的大小為2G
④ :-XX:SurvivorRatio ,設定年輕代中Eden區與Survivor區的比值
3)垃圾回收的演算法有哪些?
① 引用計數法:原理是在此物件有個引用,即增加一個計數,刪除一個引用則減少一個計數.垃圾回收時,只收集計數為0的物件.此演算法的最致命的無法處理迴圈引用的問題
②: 標記-清除 :此演算法分兩個階段,第一階段從引用的根節點開始標記所有被引用的物件,第二階段遍歷整個堆,把未標記的物件清除,此演算法需要暫停應用,同時產生記憶體碎片
③: 複製演算法 此演算法把記憶體劃分為兩個相等的區域,每次只使用一個區域,垃圾回收時,遍歷當前使用的區域,把正在使用的物件複製到另一個區域中每次演算法每次只處理正在使用的物件,因此複製的成本比較小,同時複製過去以後還能進行相應的記憶體整理,不會出現"碎片問題",此演算法的缺點也很明顯,需要兩倍的記憶體空間
④: 標記-整理:此演算法結合了"標記-清除"和:複製演算法的兩個的優點,也是分兩個階段,第一個階段從根節點開始標記所有被引用物件,第二階段遍歷整個堆,清除未標記的物件並且把存活的物件"壓縮"到堆的其中一塊,按順序排放,,此演算法避免"標記-清除"的碎片問題,同時也避免"複製"的空間問題
4)root搜尋演算法中,哪些可以作為root?

分享一份面試寶典《Java核心知識點整理.pdf》“,覆蓋了JVM、鎖、高併發、反射、Spring原理、微服務、Zookeeper、資料庫、資料結構等等”,還有Java208道面試題(含答案)加入群(Java填坑之路)789337293 即可免費獲取到!
