JVM—内存模型JMM
原文作者:qzqanlhy1314
原文地址:jvm 內存模型
目錄
一、jvm 內存模型劃分
二、虛擬機棧
三、堆
四、元數據區域
五、直接內存
一、jvm 內存模型劃分
根據JVM規范,JVM 內存共分為方法區、虛擬機棧、本地方法棧、堆、程序計數器五個部分。
jvm 1.8 內存區域劃分
方法區(線程共享)
用于存儲已經被虛擬機加載的類信息、方法、常量、靜態變量等,是被所有線程共享的一塊內存區域。這個區域的內存回收目標主要針對常量池的回收和堆類型的卸載。
了解java的類加載機制后,就會明白為什么方法區是線程共享的了,推薦閱讀:java.lang包—類加載器ClassLoader類
Java堆(線程共享)
被所有線程共享的一塊內存區域,在虛擬機啟動的時候創建,用于存放對象實例。對可以按照可擴展來實現(通過-Xmx 和-Xms 來控制)當隊中沒有內存可分配給實例,也無法再擴展時,則拋出OutOfMemoryError異常。
虛擬機棧(線程私有)
是線程私有的。每個方法在執行的時候也會創建一個棧幀,存儲了局部變量、操作數、動態鏈接、方法返回地址。每個方法從調用到執行完畢,對應一個棧幀在虛擬機棧中的入棧和出棧。通常所說的棧,一般是指在虛擬機棧中的局部變量部分。局部變量所需內存在編譯期間完成分配,如果線程請求的棧深度大于虛擬機所允許的深度,則StackOverflowError。如果虛擬機??梢詣討B擴展,擴展到無法申請足夠的內存,則OutOfMemoryError。
本地方法棧(線程私有)
和虛擬機棧類似,主要為虛擬機使用到的Native方法服務。也會拋出StackOverflowError 和OutOfMemoryError。
程序計數器(線程私有):
是當前線程鎖執行字節碼的行號治時期,每條線程都有一個獨立的程序計數器,這類內存也稱為“線程私有”的內存。正在執行java方法的話,計數器記錄的是虛擬機字節碼指令的地址(當前指令的地址)。如果是Natice方法,則為空。因為處理器在一個確定是時刻只會執行一個線程中的指令,線程切換后,是通過計數器來記錄執行痕跡的,因而可以看出,程序計數器是每個線程私有的。如果執行的是java方法,那么記錄的是正在執行的虛擬機字節碼指令的地址的地址,如果是native方法,計數器的值為空(undefined)。
二、虛擬機棧
局部變量表:
存放編譯期可知的各種基本數據類型、對象引用類型和returnAddress類型(指向一條字節碼指令的地址:函數返回地址)。long、double占用兩個局部變量控件Slot。局部變量表所需的內存空間在編譯期確定,當進入一個方法時,方法在棧幀中所需要分配的局部變量控件是完全確定的,不可動態改變大小。
異常:線程請求的棧幀深度大于虛擬機所允許的深度—StackOverFlowError,如果虛擬機??梢詣討B擴展(大部分虛擬機允許動態擴展,也可以設置固定大小的虛擬機棧),但是無法申請到足夠的內存—OutOfMemorError。
操作數棧:
后進先出LIFO,最大深度由編譯期確定。棧幀剛建立使,操作數棧為空,執行方法操作時,操作數棧用于存放JVM從局部變量表復制的常量或者變量,提供提取,及結果入棧,也用于存放調用方法需要的參數及接受方法返回的結果。操作數棧可以存放一個jvm中定義的任意數據類型的值。在任意時刻,操作數棧都一個固定的棧深度,基本類型除了long、double占用兩個深度,其它占用一個深度
動態連接:
每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調用過程中的動態連接。Class文件的常量池中存在有大量的符號引用,字節碼中的方法調用指令就以常量池中指向方法的符號引用為參數。這些符號引用,一部分會在類加載階段或第一次使用的時候轉化為直接引用(如final、static域等),稱為靜態解析,另一部分將在每一次的運行期間轉化為直接引用,這部分稱為動態連接。
方法返回地址:
當一個方法被執行后,有兩種方式退出該方法:執行引擎遇到了任意一個方法返回的字節碼指令或遇到了異常,并且該異常沒有在方法體內得到處理。無論采用何種退出方式,在方法退出之后,都需要返回到方法被調用的位置,程序才能繼續執行。方法返回時可能需要在棧幀中保存一些信息,用來幫助恢復它的上層方法的執行狀態。一般來說,方法正常退出時,調用者的PC計數器的值就可以作為返回地址,棧幀中很可能保存了這個計數器值,而方法異常退出時,返回地址是要通過異常處理器來確定的,棧幀中一般不會保存這部分信息。
方法退出的過程實際上等同于把當前棧幀出棧,因此退出時可能執行的操作有:恢復上層方法的局部變量表和操作數棧,如果有返回值,則把它壓入調用者棧幀的操作數棧中,調整PC計數器的值以指向方法調用指令后面的一條指令。
三、堆
堆是JVM內存占用最大、管理最復雜的一個區域。唯一的途徑就是存放對象實例:所有的對象實例以及數組都在堆上進行分配。jdk1.7以后,字符串常量從永久代中剝離出來,存放在堆中。堆具有進一步的內存劃分。按照GC分代手機角度劃分
- 老年代:2/3的堆空間
- 年輕代:1/3的堆空間
- eden區:8/10 的年輕代
- survivor0: 1/10 的年輕代
- survivor1:1/10的年輕代
四、元數據區域
元數據區域取代了1.7版本及以前的永久代。元數據和永久代本質上都是方法區的實現。方法區是虛擬機加載的類信息、靜態變量、常量數據。參數設置:
- -XX:MetaspaceSize=18m
- -XX:MaxMetaspaceSize=60m
五、直接內存
java.nio 中使用DirectBuffer相關使用(此處未完待續。。。。。。。。。)
總結
以上是生活随笔為你收集整理的JVM—内存模型JMM的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM—内存可见性
- 下一篇: 分布式实时计算—实时数据质量如何保障?