JVM自動內存管理機制
文章目錄
1)Java虛擬機
2)JVM內存區域的劃分
3)內存溢出
Java虛擬機
- 虛擬機給每個對象都分配了一個鎖
- 監視器
- 鎖是通過監視器實現的,監視器主要功能是監控一段代碼,確保在同一時間只有一個線程在執行
- 每個監視器都與一個對象相關聯
- 多次加鎖
- 同一個線程可以對同一個對象進行多次加鎖,每個對象維護者一個記錄着被鎖次數的計數器,當一個線程獲得鎖後,計數器自增,釋放鎖的時候,計數器自減
JVM內存區域的劃分
- 1.運行時數據區
- 線程私有數據區
- 1.程序計數器
- 當前線程所執行的字節碼的行號指示器
- 字節碼解釋器工作時,就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
- 2.虛擬機棧
- Java方法執行的內存模型
- 如果線程請求的棧深度大於虛擬機所允許的深度,將拋出
StackOverflowError
異常; - 如果虛擬機棧可動態擴展且擴展時無法申請到足夠的內存,將拋出
OutOfMemoryError
異常。
- 3.本地方法棧
- 虛擬機使用到的Native方法服務
- 1.程序計數器
- 線程共享數據區
- 1.Java堆
- 存放幾乎所有的對象實例和數組
- 可能劃分出多個線程私有的分配緩衝區(TLAB)
- 垃圾收集器管理的主要區域,“GC堆”
- 2.方法區(包含常量池)
- 存儲已被加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據
- JVM規範將方法區描述爲堆的一個邏輯部分,但它卻還有一個別名叫做Non-Heap(非堆),目的就是要和堆分開。
- 永久代是方法區的一個實現,jdk1.7的版本中,已經將原本放在永久代的字符串常量池移走。
- JDK1.8,元數據區取代了永久代。元空間的本質和永久代類似,都是對 JVM 規範中方法區的實現。不過元數據空間並不在虛擬機中,而是使用本地內存,所以1.8元空間不存在OOM內存溢出的情況。
- 運行時常量池是方法區的一部分
- 1.Java堆
- 線程私有數據區
- 2.HotSpot虛擬機對象
- 語言層面上,創建對象僅僅是一個new關鍵字
- 當虛擬機遇到一條new指令時,(對象的創建):
- 類加載檢查
- 檢查new指令的參數能否在常量池中定位到一個類的符號引用且該符號引用代表的類是否已被加載,解析和初始化,若沒有,則需要先進行類加載
- 分配內存
- 內存規整,則使用"指針碰撞"分配方式
- 內存不規整,則使用"空閒列表"分配方式
- 設置對象頭
- 類加載檢查
- 對象的內存佈局
- 對象頭
- 包含用於存儲對象自身的運行時數據
- 包含類型指針,即對象指向它的類元數據的指針
- 實例數據
- 存儲真正有效的信息
- 對象填充
- 非必需的佔位符
- 對象頭
- 對象的訪問
- 通過句柄訪問對象
- 通過直接指針訪問對象
內存溢出
- 內存溢出和內存泄露的區別
- 內存溢出(Out Of Memory)
- 指程序在申請內存時,沒有足夠的內存空間供其使用
- 內存泄露(Memory Leak)
- 程序申請內存後,由於某種原因無法釋放已申請的內存空間,導致這塊內存無法再次被利用,造成系統內存的浪費
- 內存泄露堆積會導致內存溢出
- 內存溢出(Out Of Memory)
- Java堆溢出
- Java堆用於存儲對象實例,只要不斷的創建對象,並且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,達到堆的最大容量就會產生內存溢出異常
-Xms
參數:堆的最小值,-Xmx
參數:堆的最大值
- 棧溢出
- 線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverfloeError異常
- 虛擬機在擴展棧是無法申請到足夠的內存空間,則拋出OutOfMemoryError異常
- 可以通過
-Xss
參數來指定虛擬機棧的最大深度,也可以將虛擬機棧設置爲可動態擴展。
- 方法區和運行時常量池溢出
- 方法區中保存Class對象沒有被及時回收掉或者Class信息佔用的內存超過了我們配置
-XX:PermSize
和-XX:MaxPermSize
限制方法區大小,從而間接限制其中常量池的容量
參考
《深入理解Java虛擬機》
JVM內存溢出詳解
JVM之內存溢出
堆、棧、方法區、直接內存、堆和棧區別