1. 程式人生 > >JVM內存區域劃分及垃圾回收

JVM內存區域劃分及垃圾回收

native方法 返回 局部變量 有一個 一個 靜態屬性 才會 記錄 觸發

第一部分、閑扯+概述

近來在研讀《深入理解java虛擬機》一書,讀完之後做個小結,算是記錄一下自己的學習所得,在成長的路上,只能死磕。

要理解JVM,就要先從其內存區域劃分開始,知道其由幾部分構成,再了解各部分的功能,這樣就能對其整體有一個了解。

話不多說,總體圖先呈上:

技術分享圖片

可以看到,線程私有的內存區域有虛擬機棧、本地方法棧、程序計數器,這些區域都不會出現線程安全問題;而線程共享的區域有堆、方法區。

下面對其各個分區域進行介紹。

第二部分:JVM內存區域劃分

1、首先是最簡單的程序計數器,每個線程都有一個獨立的計數器,用來記錄線程執行的虛擬機字節碼指令。另外,如果是Native方法,則計數器中的

值為undifined。

2、本地方法棧:類似於虛擬機棧,只是此處運行的是native方法

3、虛擬機棧:運行java方法的地方,基本單位是棧幀,一個方法從開始執行到結束的過程,就是一個棧幀在虛擬機棧中入棧到出棧的過程。(本來

認真準備了兩張棧幀跟局部變量表的邏輯示意圖,不知道怎麽的傳不上來,只能省去,<手動擦汗>)

3.1、局部變量表:顧名思義,是存返方法中變量的地方。

局部變量表存放變量的單位是Slot 槽,每個槽都能存放一個32位的變量,64位的由連續的兩個槽來存放。如果執行的是非static方法,則第一個槽中

存放的是當前對象的引用,即this。具體存放的引用,是直接或間接能定位到這個對象在堆中的位置的一個值,比如對象的起始地址。

3.2、操作數棧:是LIFO(後入先出)棧,虛擬機就是一個基於操作數棧的執行引擎。在處理數據中處於重要位置。一個32位的數據占據棧中一個容量。比如執行a+b時,字節碼

指令會先往棧中壓入a跟b,然後iadd指令執行時,將棧頂的兩個值a跟b彈出,相加,再壓入棧中,完成計算。

3.3、動態鏈接:存放的是方法區的運行時常量池中此棧幀對應的方法的引用

3.4、返回地址:記錄方法執行完之後返回的位置,回到這個位置程序才能正確的繼續執行下去。

4、方法區:最主要的功能,便是存放字節碼文件、Class對象、常量等

5、堆區:此部分是虛擬機中上連棧下連方法區的中樞之地,也是垃圾收集的重地,可以說基本所有的對象都存放在此區域。

   對象在堆區中,由三部分組成:對象頭、實體數據、對齊填充

對象頭:一般由兩部分組成,一部分是用於存放hash值、分代年齡、鎖標識的稱為Mark Word的區域,另一部分用於存放指向方法區中對象類型的指針(直接指針定位時有);

實體數據:存放正常的對象數據;

對齊填充:對象的內存占用大小為8字節的整數倍,如果不夠,用此部分來填充。

堆區按照垃圾收集區域,劃分為兩部分:新生代、老年代,一般默認比例是1:2,而在新生代中,又分成了三部分,分別為Eden、Survivor From 、Survivor To ,默認比例

為8:1:1。

第三部分、垃圾回收

觸發條件:當內存中可用空間不夠時才會觸發垃圾回收

什麽樣的對象會被回收:通過可達性分析算法,如果發現某個對象與GC ROOTS沒有連接,則此對象滿足被回收的條件。

GC ROOTS定義:1) 虛擬機棧中引用的對象;2)本地方法棧中引用的對象;3) 類靜態屬性引用的對象;4) 常量引用的對象

如何回收:標記清除算法、復制算法、標記整理算法、分代搜集算法,目前主流虛擬機中使用的就是分代搜集算法

分代搜集算法的過程:JVM開發團隊發現,對於新生代中大部分的對象,存活時間都很短,所以新生代使用復制算法,且工作空間與空閑空間之比為9:1(9即上面所說的一個

Eden與Survivor From之和),因為新生代絕大部分對象會被清理,清理之後還剩下的對象就放在那一份的Survivor To中,如果空間不夠,再往老年代放,另外經歷過一定次數的

新生代處理還沒被回收的對象,也會轉移到老年代中;而對於老年代,用標記整理算法或者標記清除算法,因為老年代對象數量龐雜,且大多數不滿足清理條件,所以老年代的

垃圾回收耗時長效果相對較差。新生代中Eden區域專門用於存放新對象,如果此區域內存空間不足,則觸發新生代GC,此之謂Minor GC;同樣老年代空間不足,觸發Full GC(亦

稱之為Major GC),耗時長,要盡量避免,一般來說Full GC次數一天不能超過一次。

垃圾收集器:針對新生代跟老年代都有不同的垃圾搜集器,例如針對新生代的有Serial、ParNew等,針對老年代的有CMS、Serial Old等,不同的搜集器之間只能有特定的

幾個組合(不能隨意地組合使用,會有不兼容),具體看項目實際情況而定。目前BZ所在公司用的是ParNew與CMS的組合,雖然CMS用的是標記清除算法,會使垃圾收集後的內存

碎片較多,但是萬能的前輩們早就想到了這些,提供了-XX:+UseCMSCompactAtFullCollection指令壓縮內存中的碎片,以及-XX:CMSFullGCsBeforeCompaction指令設置多少次

Full GC後整理內存碎片。

針對垃圾收集這一塊,BZ還沒有實際動手通過相關知識解決過項目中的問題,目前只是紙上談兵,後面會摸索一下如何通過工具監控項目中的內存狀況,並整理出來。學習之

路,還是要多給自己打打雞血~

JVM內存區域劃分及垃圾回收