1. 程式人生 > >JVM記憶體區域詳解

JVM記憶體區域詳解

JVM記憶體模型總體架構圖

程式計數器
多執行緒時,當執行緒數超過CPU數量或CPU核心數量,執行緒之間就要根據時間片輪詢搶奪CPU時間資源。因此每個執行緒有要有一個獨立的程式計數器,記錄下一條要執行的指令。執行緒私有的記憶體區域。如果執行的是JAVA方法,計數器記錄正在執行的java位元組碼地址,如果執行的是native方法,則計數器為空。
虛擬機器棧
執行緒私有的,與執行緒在同一時間建立。管理JAVA方法執行的記憶體模型。每個方法執行時都會建立一個楨棧來儲存方法的的變量表、運算元棧、動態連結方法、返回值、返回地址等資訊。棧的大小決定了方法呼叫的可達深度(遞迴多少層次,或巢狀呼叫多少層其他方法,-Xss引數可以設定虛擬機器棧大小)。棧的大小可以是固定的,或者是動態擴充套件的。如果請求的棧深度大於最大可用深度,則丟擲stackOverflowError;如果棧是可動態擴充套件的,但沒有記憶體空間支援擴充套件,則丟擲OutofMemoryError。
使用jclasslib工具可以檢視class類檔案的結構。下圖為棧幀結構圖:

本地方法區
和虛擬機器棧功能相似,但管理的不是JAVA方法,是本地方法,本地方法是用C實現的。

JAVA堆
執行緒共享的,存放所有物件例項和陣列。垃圾回收的主要區域。可以分為新生代和老年代(tenured)。
新生代用於存放剛建立的物件以及年輕的物件,如果物件一直沒有被回收,生存得足夠長,老年物件就會被移入老年代。
新生代又可進一步細分為eden、survivorSpace0(s0,from space)、survivorSpace1(s1,to space)。剛建立的物件都放入eden,s0和s1都至少經過一次GC並倖存。如果倖存物件經過一定時間仍存在,則進入老年代(tenured)。


方法區
執行緒共享的,用於存放被虛擬機器載入的類的元資料資訊:如常量、靜態變數、即時編譯器編譯後的程式碼。也成為永久代。如果hotspot虛擬機器確定一個類的定義資訊不會被使用,也會將其回收。回收的基本條件至少有:所有該類的例項被回收,而且裝載該類的ClassLoader被回收

垃圾回收演算法

標記-清除演算法(Mark-Sweep)
從根節點開始標記所有可達物件,其餘沒標記的即為垃圾物件,執行清除。但回收後的空間是不連續的。

複製演算法(copying)
將記憶體分成兩塊,每次只使用其中一塊,垃圾回收時,將標記的物件拷貝到另外一塊中,然後完全清除原來使用的那塊記憶體。複製後的空間是連續的。複製演算法適用於新生代,

因為垃圾物件多於存活物件,複製演算法更高效。在新生代序列垃圾回收演算法中,將eden中標記存活的物件拷貝未使用的s1中,s0中的年輕物件也進入s1,如果s1空間已滿,則進入老年代;這樣交替使用s0和s1。這種改進的複製演算法,既保證了空間的連續性,有避免了大量的記憶體空間浪費。

標記-壓縮演算法(Mark-compact)
適合用於老年代的演算法(存活物件多於垃圾物件)。
標記後不復制,而是將存活物件壓縮到記憶體的一端,然後清理邊界外的所有物件。



JVM引數:

-XX:+PrintGCDetails  列印垃圾回收資訊

-Xms 為Heap區域的初始值,線上環境需要與-Xmx設定為一致,否則capacity的值會來回飄動
-Xmx 為Heap區域的最大值
-Xss(或-ss) 執行緒棧大小(指一個執行緒的native空間)1.5以後是1M的預設大小
-XX:PermSize與-XX:MaxPermSize  方法區(永久代)的初始大小和最大值(但不是本地方法區)
-XX:NewRatio  老年代與新生代比率
-XX:SurvivorRatio  Eden與Survivor的佔用比例。例如8表示,一個survivor區佔用 1/8 的Eden記憶體,即1/10的新生代記憶體,為什麼不是1/9?因為我們的新生代有2個survivor,即S1和S22。所以survivor總共是佔用新生代記憶體的 2/10,Eden與新生代的佔比則為 8/10。
-XX:MaxHeapFreeRatio  GC後,如果發現空閒堆記憶體佔到整個預估的比例小於這個值,則減小堆空間。
-XX:MinHeapFreeRatio  GC後,如果發現空閒堆記憶體佔到整個預估的比例大於這個值,則增大堆空間。
-XX:NewSize    新生代大小

JVM區域總體分兩類,heap區和非heap區:

heap區又分:Eden Space(伊甸園)、Survivor Space(倖存者區)、Tenured Gen(老年代-養老區)。
非heap區又分:Code Cache(程式碼快取區)、Perm Gen(永久代)、Jvm Stack(Java虛擬機器棧)、Local Method Statck(本地方 法棧)。

 HotSpot虛擬機器GC演算法採用分代收集演算法:
(1)、一個人(物件)出來(new 出來)後會在Eden Space(伊甸園)無憂無慮的生活,直到GC到來打破了他們平靜的生活。GC會逐一問清楚每個物件的情況,有沒有錢(此物件的引用)啊,因為GC想賺錢呀,有錢的才可以敲詐嘛。然後富人就會進入Survivor Space(倖存者區),窮人的就直接kill掉。
(2)、並不是進入Survivor Space(倖存者區)後就保證人身是安全的,但至少可以活段時間。GC會定期(可以自定義)會對這些人進行敲詐,億萬富翁每次都給錢,GC很滿意,就讓其進入了Genured Gen(養老區)。萬元戶經不住幾次敲詐就沒錢了,GC看沒有啥價值啦,就直接kill掉了。
(3)、進入到養老區的人基本就可以保證人身安全啦,但是億萬富豪有的也會揮霍成窮光蛋,只要錢沒了,GC還是kill掉。

分割槽的目的:
新生區由於物件產生的比較多並且大都是朝生夕滅的,所以直接採用標記-清理演算法。而養老區生命力很強,則採用複製演算法,針對不同情況使用不同演算法。

非heap區域中Perm Gen中放著類、方法的定義,jvm Stack區域放著方法引數、局域變數等的引用,方法執行順序按照棧的先入後出方式。


下面我們對每一個記憶體區域做詳細介紹。 

Eden Space字面意思是伊甸園,物件被建立的時候首先放到這個區域,進行垃圾回收後,不能被回收的物件被放入到空的survivor區域。

Survivor Space倖存者區,用於儲存在eden space記憶體區域中經過垃圾回收後沒有被回收的物件。Survivor有兩個,分別為To Survivor、 From Survivor,這個兩個區域的空間大小是一樣的。執行垃圾回收的時候Eden區域不能被回收的物件被放入到空的survivor(也就是To Survivor,同時Eden區域的記憶體會在垃圾回收的過程中全部釋放),另一個survivor(即From Survivor)裡不能被回收的物件也會被放入這個survivor(即To Survivor),然後To Survivor 和 From Survivor的標記會互換,始終保證一個survivor是空的。

這裡寫圖片描述 
Eden Space和Survivor Space都屬於新生代,新生代中執行的垃圾回收被稱之為Minor GC(因為是對新生代進行垃圾回收,所以又被稱為Young GC),每一次Young GC後留下來的物件age加1。注:GC為Garbage Collection,垃圾回收。

Old Gen老年代,用於存放新生代中經過多次垃圾回收仍然存活的物件,也有可能是新生代分配不了記憶體的大物件會直接進入老年代。經過多次垃圾回收都沒有被回收的物件,這些物件的年代已經足夠old了,就會放入到老年代。當老年代被放滿的之後,虛擬機器會進行垃圾回收,稱之為Major GC。由於Major GC除併發GC外均需對整個堆進行掃描和回收,因此又稱為Full GC。

heap區即堆記憶體,整個堆大小=年輕代大小 + 老年代大小。堆記憶體預設為實體記憶體的1/64(<1GB);預設空餘堆記憶體小於40%時,JVM就會增大堆直到-Xmx的最大限制,可以通過MinHeapFreeRatio引數進行調整;預設空餘堆記憶體大於70%時,JVM會減少堆直到-Xms的最小限制,可以通過MaxHeapFreeRatio引數進行調整。

下面我們來認識下非堆記憶體(非heap區) 
Code Cache程式碼快取區,它主要用於存放JIT所編譯的程式碼。CodeCache程式碼緩衝區的大小在client模式下預設最大是32m,在server模式下預設是48m,這個值也是可以設定的,它所對應的JVM引數為ReservedCodeCacheSize 和 InitialCodeCacheSize,可以通過如下的方式來為Java程式設定。

-XX:ReservedCodeCacheSize=128m
  • 1
  • 1

CodeCache快取區是可能被充滿的,當CodeCache滿時,後臺會收到CodeCache is full的警告資訊,如下所示: 

      “CompilerThread0” java.lang.OutOfMemoryError: requested 2854248 bytes for Chunk::new. Out of swap space?

注:JIT編譯器是在程式執行期間,將Java位元組碼編譯成平臺相關的二進位制程式碼。正因為此編譯行為發生在程式執行期間,所以該編譯器被稱為Just-In-Time編譯器。

Perm Gen全稱是Permanent Generation space,是指記憶體的永久儲存區域,因而稱之為永久代。這個記憶體區域用於存放Class和Meta的資訊,Class在被 Load的時候被放入這個區域。因為Perm裡儲存的東西永遠不會被JVM垃圾回收的,所以如果你的應用程式LOAD很多CLASS的話,就很可能出現PermGen space錯誤。預設大小為實體記憶體的1/64。