Java虛擬機在執行Java程序過程當中把其所管理的內存劃分紅若干個不一樣的數據區域。程序員
當前線程所執行的字節碼的指示器。經過改變這個計數器的值來選取下一個須要執行的字節碼指令,分支、循環、跳轉、異常、線程恢復等都須要這個計數器完成。算法
每一個線程一個獨立的程序計數器,各線程之間互不影響,獨立存儲。安全
執行Java方法時:正在執行虛擬字節碼的指令地址
執行Native方法:值爲空(Undefined)
複製代碼
惟一一個Java虛擬機規範中沒有規定OutOfMemoryError的區域併發
線程私有,生命週期和線程一致。函數
描述的是Java方法執行的內存模型:方法執行同時建立一個棧幀,方法從調用到執行完成,就是一個棧幀在虛擬機棧中入棧到出棧的過程。佈局
其中棧幀用於存儲:局部變量表、操做數棧、動態連接、方法出口等信息。spa
局部變量表中存放了編譯期間可知的基本數據類型和對象引用。線程
64位的long和double類型的數據會佔2個局部空間變量,其他類型只佔1個。因局部變量表中所需內存是在編譯期間完成的,因此這個方法在幀中須要分配多少局部變量空間是肯定的。指針
這部分區域異常:StackOverFlowError和OutOfMemoryError 、code
與虛擬機棧的做用相似。本地方法棧是爲Native方法服務。拋出異常與虛擬機棧一致。
堆是Java虛擬機中內存最大的一塊。線程共享,虛擬機啓動時建立。存放對象實例。
幾乎全部的對象都在這裏分配內存。【隨着JIT編譯器的發展和逃逸分析技術的成熟,棧上分配和標量替換,對象不必定在堆中分配】
堆是垃圾收集管理器的主要區域,也叫GC堆。細分爲:新生代、老年代。eden->from survivor->to survivor
經過-Xmx和-Xms控制擴展,沒法擴展時拋出OutOfMemoryError異常
線程共享。存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯的代碼。也叫非堆,也稱永久代。
-XX:MaxPermSize
內存回收目標:常量池的回收和類的卸載
OutOfMemoryError
方法區的一部分
OutOfMemoryError
不是虛擬機運行時區的一部分。
JDK1.4新加入的NIO類,引入Channel和Buffer的I/O方式,使用Native函數庫直接分配堆外內存,使用DirectByteBuffer做爲這塊內存的引用進行操做。
受本機內存大小和處理器尋址空間的限制
OutOfMemoryError
當遇到new指令時
首先檢查這個指令的參數是否能在常量池中定位到一個類的符號引用。【並檢查該符號引用表明的類是否已被加載、解析、初始化過,若沒有則執行類加載】
類加載經過以後,爲新生對象分配內存
內存分配完成後,需將分配到的內存空間都初始化爲零值,不包括對象頭
接下來,虛擬機要對對象進行必要的設置,設置對象頭。如這個對象是那個類的實例。如何才能找到類的元數據信息、對象的哈希碼、對象的GC分代年齡等信息。
複製代碼
分配內存方式:
指針碰撞
空閒列表
複製代碼
當使用Serial、ParNew等帶compact過程的收集器時:分配算法是指針碰撞
使用CMS基於Mark-Sweep算法的收集器時:分配算法採用空閒列表
分配對象內存空間併發下線程安全問題:
採用CAS加上失敗重試保證更新的原子性
把內存分配動做按線程劃分在不一樣的空間進行,即每一個線程在Java堆中預先分配一塊小內存,成爲本地線程分配緩衝區【TLAB】 -XX:+、-UseTLAB
複製代碼
對象建立完成以後還有進行init,按照程序員的意願進行初始化。
對象內存佈局分爲三塊
對象頭、實例數據、對齊填充
複製代碼
對象頭包含兩部分:
存儲對象自身的運行數據
類型指針
複製代碼
包含哈希碼、GC分代年齡、鎖狀態標誌、線程持有鎖、偏向線程ID、偏向時間戳等。也稱Mrak Word
對象指向它的類元數據的指針。虛擬機經過這個指針來肯定對象是屬於哪一個類的實例
對象真正存儲的有效信息,各類類型的字段內容。
不是必然存在的。起着佔位符的做用。
對象起始地址必須是8字節的整數倍。當對象實例數據部分沒有對齊時,就須要對齊填充了。
對象的訪問定位目前有兩種流行的方式:
使用句柄
直接指針
複製代碼
使用句柄好處:棧中引用存儲的是穩定的句柄地址,對象被移動時只會改變句柄中的實例數據指針,引用自己不需變化
使用直接引用好處:速度快,節省一次指針定位的時間開銷