Java記憶體區域

Java記憶體區域

1.Java記憶體區域劃分

JVM在執行Java程式的過程中會把它管理的記憶體區域劃分成兩類用途不同的數據區:

  • 執行緒共有(執行緒共享)區域——堆和方法區

  • 執行緒私有(執行緒隔離)區域——程式計數器、虛擬機棧和本地方法棧

image-20210526164944774

2.執行緒共享數據區

image-20210526185840704

用途 完成幾乎所有Java實例對象的記憶體分配,進行垃圾回收的主要記憶體區域
劃分 年輕代8 : 1 : 1(Eden區 : From Survivor 0 : To Survivor 1 區)、老年代
記憶體空間大小
垃圾回收模式 以主流的HotSpot為例,採用分代回收策略,對於年輕代的對象採取複製演算法,對老年代採取標記-清除或者標記-整理演算法
異常情況 堆中沒完成實例分配,且堆此時無法再擴展,JVM會拋出OOM異常
對象分配策略

當有對象需要分配時,一個對象永遠優先被分配在年輕代的 Eden 區,等到 Eden 區域記憶體不夠時,Java 虛擬機會啟動垃圾回收。此時 Eden 區中沒有被引用的對象的記憶體就會被回收,在一次新生代垃圾回收後,Eden區中所有存活的對象都會被複製到To Survivor區,而在From Survivor區中,仍存活的對象會根據它們的年齡值決定去向,年齡值達到年齡閥值(默認為15,新生代中的對象每熬過一輪垃圾回收,年齡值就加1,GC分代年齡存儲在對象的header中)的對象會被移到老年代中,沒有達到閥值的對象會被複制到To Survivor區。接著清空Eden區和From Survivor區,新生代中存活的對象都在To Survivor區。接著, From Survivor區和To Survivor區會交換它們的角色,也就是新的To Survivor區就是上次GC清空的From Survivor區,新的From Survivor區就是上次GC的To Survivor區,總之,不管怎樣都會保證To Survivor區在一輪GC後是空的。GC時當To Survivor區沒有足夠的空間存放上一次新生代收集下來的存活對象時,需要依賴老年代進行分配擔保,將這些對象存放在老年代中。

方法區

用途 存儲已被虛擬機載入的類的結構資訊(欄位和方法數據、構造方法)、常量、靜態變數、即時編譯器編譯後的程式碼快取等數據,像是堆的一個邏輯部分
異常情況 方法區無法滿足新的記憶體分配需求時,拋出OOM異常

3.執行緒私有數據區

程式計數器

用途 指示當前執行緒所執行的位元組碼的行號指示器。位元組碼解釋器工作時就是通過改變程式計數器的值來選取下一條需要執行的位元組碼指令,程式控制中的分支、循環、跳轉、異常處理、執行緒恢復等都依賴程式計數器
私有性質 JVM多執行緒通過執行緒切換並分配CPU時間實現的,任一確定的時刻,一個CPU指揮執行一條執行緒中的指令。所以為了執行緒切換後能恢復到正確的執行位置,每個執行緒都需要一個獨立的程式計數器。
記憶體空間大小
生命周期 隨著執行緒的創建而創建,隨著執行緒的消亡而消亡
真實值 如果執行的是Java方法,那麼計數器記錄的就是正在執行的JVM位元組碼指令的地址;如果執行的是本地(Native)方法,那麼計數器的值則應該為空(Undefined)
異常情況 唯一一個不會出現 OutOfMemoryError(OOM) 的記憶體區域

虛擬機棧

含義 描述Java方法執行的執行緒記憶體模型,每個Java方法被執行時,虛擬機都會同步創建一個棧幀
組成 Java虛擬機棧由一個個棧幀組成
棧幀 存儲了局部變數表、操作數棧、動態鏈接、方法出口等資訊
局部變數表 主要存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference 類型,它不同於對象本身,可能是一個指向對象起始地址的引用指針,也可能是指向一個代表對象的句柄或其他與此對象相關的位置)和returnAddress類型(指向了一條位元組碼指令的地址)。
生命周期 和執行緒相同
異常情況 如果執行緒請求的棧深度大於虛擬機允許的最大深度,就會拋出StackOverflowError異常(棧溢出異常);如果虛擬機在動態擴展棧時無法申請到足夠的記憶體空間,則拋出OutOfMemoryError異常

本地方法棧

為虛擬機使用到的本地方法服務。和Java虛擬機棧發揮的作用非常相似,和虛擬機棧一樣,本地方法棧也會在棧深度溢出或者棧動態擴展失敗時分別拋出StackOverflowError異常和OutOfMemoryError異常。

參考及部分圖源

Tags: