JVM基本結構及記憶體模型及優化

  • 2019 年 10 月 4 日
  • 筆記

JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用於計算設備的規範,它是一個虛構出來的電腦,是通過在實際的電腦上模擬模擬各種電腦功能來實現的。作為一個Java開發者,這是必須要掌握的知識點。

一,JVM的基本結構

JVM啟動後,對作業系統來說,JVM是一個的進程,這個進程的基本結構如上圖所示。它包括:

  • 類載入器子系統
  • 運行時數據區
  • 執行引擎

運行時數據區是JVM從作業系統申請來的堆空間和作業系統給JVM分配的棧空間的總稱。也是JVM記憶體結構所在位置。

二,JVM的記憶體模型

根據java虛擬機規範,java虛擬機管理的記憶體將分為下面五大區域。分為:虛擬機棧,堆,方法區,程式計數器,本地方法棧。

1,程式計數器

是當前執行緒鎖執行位元組碼的行號治時期,每條執行緒都有一個獨立的程式計數器,這類記憶體也稱為「執行緒私有」的記憶體。正在執行java方法的話,計數器記錄的是虛擬機位元組碼指令的地址(當前指令的地址)。如果是Natice方法,則為空。

2,Java 虛擬機棧

它是執行緒私有的。每個方法在執行的時候也會創建一個棧幀,存儲了局部變數,操作數,動態鏈接,方法返回地址。每個方法從調用到執行完畢,對應一個棧幀在虛擬機棧中的入棧和出棧。通常所說的棧,一般是指在虛擬機棧中的局部變數部分。局部變數所需記憶體在編譯期間完成分配,如果執行緒請求的棧深度大於虛擬機所允許的深度,則StackOverflowError。如果虛擬機棧可以動態擴展,擴展到無法申請足夠的記憶體,則OutOfMemoryError。

3,本地方法棧

執行緒私有,和虛擬機棧類似,主要為虛擬機使用到的Native方法服務。也會拋出StackOverflowError 和OutOfMemoryError。

4,方法區

被所有方法執行緒共享的一塊記憶體區域。用於存儲已經被虛擬機載入的類資訊,常量,靜態變數等。這個區域的記憶體回收目標主要針對常量池的回收和堆類型的卸載。

5,堆記憶體

堆是JVM記憶體佔用最大,管理最複雜的一個區域。唯一的途徑就是存放對象實例:所有的對象實例以及數組都在堆上進行分配。jdk1.7以後,字元串常量從永久代中剝離出來,存放在堆中。堆具有進一步的記憶體劃分。即新生代和老年代,劃分如下圖所示:

按照GC分代收集的角度堆記憶體劃分:

  • 老年代:2/3的堆空間
  • 年輕代:1/3的堆空間
  • eden區:8/10 的年輕代
  • survivor0: 1/10 的年輕代
  • survivor1: 1/10的年輕代

堆記憶體中的記憶體分配:

  • 清理年輕代:minor GC;
  • 清理老年代:major GC;
  • 清理整個堆空間:full GC;

堆記憶體中年輕代和老年代的記憶體分配及使用規則:

1,新創建的對象主要分配在年輕代的Eden區;

被new的對象優先分配到年輕代的Eden區,如果Eden區沒有足夠大小的連續記憶體空間分配時,Jvm就會觸發一次minor GC,經過minor GC回收存活下來的對象被分配到Survivor區,如果Survivor區不夠安置,Jvm會通過「空間分配擔保機制」提前轉移對象到老年代。

2,大對象直接進入老年代

那些需要大量連續記憶體空間的對象,如數組或很長的字元串,大小超過虛擬機提供的闕值-XX:PretenureSizeThreshold(默認值為0)時,對象直接分配到老年代,避免在新生代Eden和兩個Survivor區之間發生大量的記憶體複製(新生代採用複製演算法進行垃圾回收)。

3,年輕代長期存活的對象會被晉陞到老年代

虛擬機記憶體回收時,為了能夠識別哪些對象該繼續留在新生代(Survivor區),那些對象應該轉移到老年代,它給每個對象定義了一個年齡計數器。每當對象在Survivor區的From<—>To區複製一次,年齡計數器就+1,對年齡超過 -XX:MaxTenurigThreshold設置的值時,對象被轉移到老年代。對象年齡最大值為15。注意,這裡不是所有對象的年齡都到設置的闕值才轉移,當Survivor區空間記憶體不足時,對象也會提前進去老年區。

4,動態對象年齡判斷

虛擬機不是永遠等到對象年齡達到闕值後才轉移到老年代。當Survivor中相同年齡的所有對象的大小總和大於Survivor的一半時,那些年齡大於等於此相同年齡的所有對象將會直接提前進入老年區。

註:-XX:MaxTenurigThreshold未設置時,默認為15。