1、執行時資料區域
Java虛擬機器會將記憶體區域劃分為幾個區域,每個區域儲存不同型別的資料或承擔不同的功能。
PC,堆-Java堆,棧-虛擬機器棧、本地方法棧,方法區、直接記憶體。
當類被例項化或static方法被呼叫時,Class檔案被載入,關於類的資訊儲存在方法區裡(有了模子)。虛擬機器獲得了類的相關資訊,就可以在Java堆裡建立例項物件了。類方法被呼叫時,便會在Java棧中產生一個棧幀來記錄呼叫的上下文資訊,其中區域性變量表中儲存了指向Java堆中例項物件的引用以便操縱物件。當程式的執行需要呼叫由其他語言編寫的方法時(系統呼叫等的dll),就需要本地方法棧來服務。
1.1、程式計數器PC
下一條指令(位元組碼)的地址,通過改變PC的值來完成程式狀態切換。多執行緒中,每個執行緒都有自己的執行流程,因此它們要有自己的程式計數器PC。
執行Native方法時,PC為空(Undefined)。
沒有OutOfMemoryError。
1.2、Java虛擬機器棧
描述Java方法的執行,執行緒私有,生命週期與執行緒相同。每個方法被呼叫時,都會在虛擬機器棧中建立一個棧幀(Stack Frame),其中記錄了方法執行的上下文資訊(區域性變量表、運算元棧、動態鏈 接、方法返回等)。通常所說的堆記憶體、棧記憶體一般指Java堆和虛擬機器棧(區域性變量表部分)。
區域性變量表存放編譯時確定的各種基本資料型別(boolea...、reference、returnAddress),區域性變量表所需空間在編譯時完成分配,執行期間不會改變。
棧深度超過最大值時-StackOverflowError;擴充套件時無法申請到足夠記憶體-OutOfMemoryError。
1.3、本地方法棧
虛擬機器棧為虛擬機器執行Java方法(Java位元組碼)服務,而本地方法棧為虛擬機器使用到的Native方法(本地方法)服務。
什麼是本地方法?——"A native method is a Java method whose implementation is provided by non-java code."
Native Method就是一個java呼叫非java程式碼的介面。一個Native Method是這樣一個java的方法:該方法的實現由非java語言實現,比如C。
StackOverflowError,OutOfMemoryError。
1.4、Java堆
存放類的例項物件,由所有執行緒共享。所有的物件例項以及陣列都要在堆上分配(陣列也是一種物件,不同型別陣列會有不同的類)。
Java堆也是GC的主要管理區域,從垃圾回收角度看,Java堆可以細分為Young Generation(新生代)和Old Generation(老年代);
更加細分可以分為Eden空間、From Survivor空間和To Survivor空間等。劃分的目的只是為了更好的進行垃圾回收。
邏輯地址連續,實體地址可不連續。
堆無法擴充套件時丟擲OutOfMemoryError。
關於新生代、老年代、永久代更詳細的介紹可參見:https://www.jianshu.com/p/d3a0b4e36c28
1.5、方法區
儲存被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料,由所有執行緒共享。
常量池:Class檔案一部分,存放編譯期生成的各種字面量和符號引用。在類載入後存放入執行時常量池。
執行時常量池:方法區的一部分,相較於常量池更動態,執行期間也可能有新的常量放入。
1.6、直接記憶體
Direct Memory,不是虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域。
NIO中使用的基於Channel和Buffer的IO方式可使用Native函式庫直接分配堆外記憶體,然後通過一個儲存在Java堆中的DirectByteBuffer物件操縱。避免了在Java堆和Native堆中來回複製,提高了效能。
1.7、HotSpot物件建立過程
1、new
2、在常量池中尋找符號引用
3、檢查符號代表的類是否被載入、解析和初始化過,沒有載入則進行載入,找不到Class檔案則報ClassNotFind錯誤
4、為新生物件分配記憶體:
緊縮過的記憶體採用Bump the Point方式,即指標向空閒區域移動物件大小長度
記憶體沒有緊縮過,不規整,則採用Free List方式,在記錄的空閒塊中尋找相應大小的塊來分配
保證記憶體分配時的執行緒安全性:
1、採用同步處理,Compare And Set 加失敗重試。
2、為每個執行緒預分配單獨的空間,執行緒在其自己的空間上建立物件,但是,執行緒空間擴容時還是要進行同步處理。
5、將分配出的空間初始化為0(不包括物件頭)
6、對物件頭中資訊進行必要設定
7、執行<init>進行物件初始化
1.8、物件記憶體佈局
Header(物件頭)、(Instance Data)例項資料、Padding(對其填充)。
Header分為兩部分,第一部分儲存物件的自身的執行時資料(HashCode、GC分代年齡、鎖狀態標記等);第二部分是型別指標,指向方法區中類的元資料資訊。如果物件是一個數組,則還要有長度資訊。
Instance Data部分儲存物件的例項資訊,包括它自己的以及繼承而來的所有資訊。儲存順序受虛擬機器分配策略引數和欄位在原始碼中定義順序影響。
Padding只起佔位符作用,受JVM對物件起始地址的要求影響。
1.9、物件訪問定位
物件是在堆上建立的,而操作物件需要通過Java棧上的reference資料來進行。
主流方式有控制代碼和直接指標兩種。
控制代碼池中的控制代碼儲存了物件例項資料地址以及物件的型別資料(元資料)地址,reference中儲存物件控制代碼的地址即可定位到物件和其元資料。
採用直接指標時,reference指向物件地址,物件的物件頭中的型別指標指向其元資料地址。
控制代碼的優勢是穩定,物件位置移動時,只需改動控制代碼中的資料,而reference本身不用改變。
直接指標直接指向物件,因此訪問速度快。
1.10、記憶體區域相關的JVM引數
Xms20m 堆的最小值設為20m
Xmx20m 堆的最大值設為20m
XX:+HeapDumpOnOutOfMemoryError 讓虛擬機器在出現記憶體溢位時Dump出當前記憶體堆轉儲快照以便時候分析
Xss128k 設定棧容量為128k
Xoss128k 設定本地方法棧大小為128k(HotSpot不區分虛擬機器棧和本地方法棧,因此該方法無用)
XX:MaxPermSize 設定方法區最大值
XX:PermSize 設定方法區大小
XX:MaxDirectMemorySize 設定最大直接記憶體大小,如不指定則與Java最大值一樣