Java 記憶體區域
Heap
線程公有
存放執行個體對象
是GC主要管理區域,是以可以更細緻的劃分為:新生代、老年代
再細緻一點劃分:Eden區、From Survivor區、To Survivor區
記憶體空間:可以實體上不連續、邏輯上連續即可。
Method Area
主要存儲:類資訊、常量、靜态變量、編譯後的代碼
運作時常量池
主要存儲:編譯期的字面量以及符号引用
具有動态性,即可以在運作時将常量放入池中。
VM Stack
線程私有
主要包括:
局部變量表:存放編譯期的各種基本資料類型、對象引用、returnAddress類型
操作數棧:每一個元素可以為任意的java類型,32位資料類型所占容量為1,64位資料類型所占容量為2
動态連接配接:class檔案的常量池中有大量的符号引用,這些符号引用有一部分是在類加載階段或者在第一次使用的時候就轉換為直接引用,這部分成為靜态解析。另一部分是每一次運作的時候轉化為直接引用,這部分即為動态連接配接。
方法出口:例如A方法中調用了B方法,B方法的傳回值壓入A方法的棧幀中。
Native Method Stack
與VM Stack相似,唯一差別在于該棧為Native方法服務。
Hot Spot 将VM Stack 與 Native Method Stack 合而為一。
Program Counter Register
用于記錄線程執行位元組碼的指令的位址。
Direct Memory
NIO中使用直接記憶體,提高效率。
對象建立過程
首先當虛拟機遇到一條new指令時,先去檢查該符号引用代表的類是否已經完成類加載,若未完成,則執行以下步驟
類加載
為對象配置設定記憶體
配置設定方式:指針碰撞/空閑清單
線程安全:CAS解決
虛拟機初始化記憶體空間
虛拟機對對象進行必要的設定
執行完成初始化
對象建立完成
對象記憶體布局
第一部分用于存儲對象自身的運作時資料,如哈希碼(HashCode)、GC 分代年齡、鎖狀态标志、線程持有的鎖、偏向線程
ID、偏向時間戳、對象分代年齡,這部分資訊稱為“Mark Word”;Mark Word
被設計成一個非固定的資料結構以便在極小的空間記憶體儲盡量多的資訊,它會根據自己的狀态複用自己的存儲空間。
第二部分是類型指針,即對象指向它的類中繼資料的指針,虛拟機通過這個指針來确定這個對象是哪個類的執行個體;
如果對象是一個 Java 數組,那在對象頭中還必須有一塊用于記錄數組長度的資料。因為虛拟機可以通過普通 Java 對象的中繼資料資訊确定 Java
對象的大小,但是從數組的中繼資料中無法确定數組的大小。
在 32 位系統下,存放 Class 指針的空間大小是 4 位元組,Mark Word 空間大小也是4位元組,是以頭部就是 8 位元組,如果是數組就需要再加
4 位元組表示數組的長度。
在 64 位系統及 64 位 JVM 下,開啟指針壓縮,那麼頭部存放 Class 指針的空間大小還是4位元組,而 Mark Word 區域會變大,變成
8 位元組,也就是頭部最少為 12 位元組,如下表所示:

鄭州人流醫院 http://mobile.zzzzyy120.com/
壓縮指針:開啟指針壓縮使用算法開銷帶來記憶體節約,Java 對象都是以 8 位元組對齊的,也就是以 8 位元組為記憶體通路的基本單元,那麼在地理處理上,就有
3 個位是空閑的,這 3 個位可以用來虛拟,利用 32 位的位址指針原本最多隻能尋址 4GB,但是加上 3 個位的 8 種内部運算,就可以變化出 32GB
的尋址。
對象通路定位
兩種方式:
句柄池:引用中存儲的是句柄位址,當執行個體對象移動時,隻需要改變句柄對應的指針,不需要改變引用本身,比較穩定。
直接指針:速度快,節省了一次指針定位的開銷。
常用指令
invokeinterface:用以調用接口方法,在運作時搜尋一個實作了這個接口方法的對象,找出适合的方法進行調用。
invokevirtual:指令用于調用對象的執行個體方法,根據對象的實際類型進行分派
invokestatic:用以調用類方法
invokespecial:指令用于調用一些需要特殊處理的執行個體方法,包括執行個體初始化方法、私有方法和父類方法。
invokedynamic:JDK1.7新加入的一個虛拟機指令,相比于之前的四條指令,他們的分派邏輯都是固化在JVM内部,而invokedynamic則用于處理新的方法分派:它允許應用級别的代碼來确定執行哪一個方法調用,隻有在調用要執行的時候,才會進行這種判斷,進而達到動态語言的支援。
基于棧的指令集 && 基于寄存器的指令集
java采用的是基于棧的指令集,這種指令集依賴操作數棧進行工作
優點:
可移植:由于寄存器是由硬體直接提供的,是以程式如果依賴寄存器不可避免的會受到硬體的限制
程式代碼緊湊
編譯器實作簡單
缺點:
速度慢
指令數量多:完成相同功能所需的指令比寄存器架構多,因為光是入棧、出棧就已經很多指令了
記憶體通路多:頻繁的棧通路意味着頻繁的記憶體通路,而對于處理器來說,記憶體始終是速度的瓶頸。
Java記憶體溢出異常
記憶體溢出
堆上無記憶體可完成執行個體配置設定且堆無法擴充時:java.lang.OutOfMemoryError: Java heap space
方法區以及記憶體的常量池無法滿足記憶體配置設定需求時:java.lang.OutOfMemoryError: PermGen space
棧擴充時無法申請足夠記憶體:java.lang.StackOverflowError
記憶體洩漏
代碼設計引起的程式動态配置設定的記憶體沒有釋放,導緻該部分記憶體不可用
記憶體溢出與記憶體洩漏的差別
記憶體洩漏是導緻記憶體溢出的原因之一,記憶體洩漏積累起來就會導緻記憶體溢出
記憶體洩漏可以通過完善代碼來解決,記憶體溢出無法徹底避免,隻能通過配置來減少發生的頻率
記憶體洩漏記憶體溢出的檢查
性能監測工具:
JProfiler
Optimizeit Profiler
Eclipse Memory Analyzer
EclipseMAT
JProbe