1. 程式人生 > >JVM內存區域劃分及GC簡述

JVM內存區域劃分及GC簡述

const .com 也會 位置 虛擬機 規範 enc RR 卸載

  通常情況下,JVM(Java Virtual Machine,Java虛擬機)將內存區域劃分為以下5個部分:  

    1.Method Area(Non-Heap)(方法區) ——線程共享
    2.Heap(堆) ——線程共享
    3.Program Counter Register(程序計數器) ——非線程共享
    4.VM Stack(虛擬機棧)——非線程共享
    5.Native Method Stack ( 本地方法棧 )——非線程共享

  JVM運行的時候會分配好Method Area(方法區)和Heap(堆);JVM 每遇到一個線程,就為其分配一個Program Counter Register(程序計數器), VM Stack(虛擬機棧)和Native Method Stack (本地方法棧), 當線程終止時,三者(虛擬機棧,本地方法棧和程序計數器)所占用的內存空間也會被釋放掉。
  非線程共享的那三個區域的生命周期與所屬線程相同,而線程共享的區域與Java程序運行生命周期相同,這也是GC只發生在線程共享的區域(大部分發生在Heap上)的原因。

  技術分享圖片

  一.線程共享的內存區域

  1.Method Area(方法區)

  (1)《Java虛擬機規範》只是規定了有方法區這麽個概念和它的作用,但是並沒有規定如何去實現它。一般來說,在Java8以前,方法區通過永久代(PermGen)實現;從Java8開始,Java廢棄了永久代(方法區的實現),並替換為Metaspace(元空間,位於本地內存中)。方法區內很少發生垃圾回收,在這裏進行的GC主要是方法區裏的常量池和類型的卸載。

元空間是方法區的在HotSpot jvm 中的實現,方法區主要用於存儲類的信息、常量池、方法數據、方法代碼等。方法區邏輯上屬於堆的一部分,但是為了與堆進行區分,通常又叫“非堆”。
元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地內存。,理論上取決於32位/64位系統可虛擬的內存大小。

(2)方法區主要用來存儲已被虛擬機加載的類信息、常量、靜態變量和即時編譯後的代碼等數據。
(3)方法區裏有一個運行時常量池(Runtime Constant Pool),用於存放靜態編譯產生的字面量和符號引用。運行時生成的常量也會存在這個常量池中。比如String類的intern()方法。

  2.Heap(堆)

  堆空間在虛擬機啟動時創建,幾乎所有的對象實例都在這裏創建,因此該區域經常發生垃圾回收操作。
  堆空間分為新生代(有時也稱為“年輕代”)和老年代。剛創建的對象存放在新生代,而老年代中存放生命周期長久的實例對象。年輕代中又被分為Eden區(聖經中的伊甸園)和兩個Survivor區(From Space和To Space)。新的對象分配是首先放在Eden區,Survivor區作為Eden區和Old區的緩沖,在Survivor區的對象經歷若幹次收集仍然存活的,就會被轉移到老年代。

技術分享圖片

  在JDK1.2之前,Java中引用的定義很傳統:如果引用類型的數據中存儲的數值代表的是另一塊內存的起始地址,就稱這塊內存代表著一個引用。這種定義很純粹,也過於狹隘,一個對象只有被引用或者沒被引用兩種狀態。

  在JDK1.2之後,Java對引用的概念進行了擴充,將引用分為強引用、軟引用、弱引用、虛引用4種,這4種引用強度依次減弱。
  ①強引用
  代碼中普遍存在的類似"Object obj = new Object()"這類的引用,只要強引用還存在,GC 垃圾收集器永遠不會回收掉被引用的對象。
  ②軟引用
  描述有些還有用但並非必需的對象。在系統將要發生內存溢出異常之前,將會把這些對象列進回收範圍進行二次回收。如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。Java中的類SoftReference表示軟引用。
  ③弱引用
  描述非必需對象。被弱引用關聯的對象只能生存到下一次垃圾回收之前,垃圾收集器工作之後,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。Java中的類WeakReference表示弱引用。
  ④虛引用
  這個引用存在的唯一目的就是在這個對象被收集器回收時收到一個系統通知,被虛引用關聯的對象,和其生存時間完全沒關系。Java中的類PhantomReference表示虛引用。

技術分享圖片

  GC(Garbage Collector,垃圾回收)在堆中進行垃圾回收時,當一個對象大於eden區而小於old區(老年代)時,GC會將其直接扔到old區。當對象大於old區時,會直接拋出OutOfMemoryError(OOM)。

  二.線程私有的內存區域

  1.VM Stack(虛擬機棧)

  虛擬機棧也就是我們平常所稱的棧內存,它為java方法服務,每個方法在執行的時候都會創建一個棧幀,用於存儲局部變量表、操作數棧、動態鏈接和方法出口等信息。  

棧幀是一個內存區塊,是一個數據集,是一個有關方法(Method)和運行期數據的數據集,當一個方法 A 被調用時就產生了一個棧幀 F1,並被壓入到棧中,A 方法又調用了 B 方法,於是產生棧幀 F2 也被壓入棧,執行完畢後,先彈出 F2棧幀,再彈出 F1 棧幀,遵循“先進後出”原則。
棧幀保存了創建棧幀的方法的返回地址和局部變量。每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調用過程中的動態連接。動態鏈接就是將常量池中的符號引用在運行期轉化為直接引用。

  棧內存的生命期是跟隨線程的生命周期的,線程結束棧內存也就釋放,對於棧來說不存在垃圾回收問題,只要線程一結束,該棧就 Over,所以不存在垃圾回收。

  局部變量表存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象的引用(reference類型,不等同於對象本身,根據不同的虛擬機實現,可能是一個指向對象起始地址的引用指針,也可能是一個代表對象的句柄或者其他與對象相關的位置)和 returnAdress類型(指向下一條字節碼指令的地址)。局部變量表所需的內存空間在編譯期間完成分配,在方法運行之前,該局部變量表所需要的內存空間是固定的,運行期間也不會改變。

  2.Native Method Stack(本地方法棧)

  本地方法棧和虛擬機棧類似,只不過本地方法棧為Native方法服務。

  3.Program Counter Register(程序計數器)

  代表著當前線程所執行字節碼的行號指示器。分支、循環、跳轉、異常處理和線程恢復等功能都需要依賴這個計數器完成。程序計數器是唯一一個java虛擬機規範沒有規定任何OOM(Out Of Memory)情況的區域。

JVM內存區域劃分及GC簡述