JAVA虚拟机 安全区域_Java虚拟机的内存区域
2020年12月10日 閱讀 186 關注
Java虛擬機的內存區域
最近在看《深入理解Java虛擬機》,故此寫下自己的學習筆記。
JVM 運行時數據區域
Java 虛擬機在執行 Java 程序的過程中會把它所管理的內存劃分為五個不同的數據區域(如圖所示)。
紅色邊框的是由所有線程共享的數據區
藍色邊框的是線程隔離的數據區
除了程序計數器之外,其他四個區域都可能會出現 OutOfMemoryError 異常。
程序計數器
程序計數器是一塊較小的內存空間
是當前線程所執行的字節碼的行號顯示器
字節碼解釋器就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令的
執行 Java 方法和執行 Native 方法的區別:
執行 Java 方法時,計數器記錄虛擬機正在執行的字節碼指令的地址
執行 Native 方法時,無記錄,也就是計數器的值為空(Undefined)
程序計數器是唯一一個不會出現 OOM 的區域。
Java 虛擬機棧
每個 Java 方法被執行的時候,Java 虛擬機都會同步創建一個棧幀用于存儲局部變量表、操作數棧、動態連接、方法出口等信息
每個方法被調用一直到執行完畢的過程,都對應著一個棧幀在 Java 虛擬機棧中從入棧到出棧的過程
Java 虛擬機棧服務于 Java 方法
可能出現的異常:
StackOverflowError:線程請求的棧深度 大于 Java 虛擬機所允許的深度時
OutOfMemoryError:在 Java 虛擬機棧容量可動態擴展的情況下,當棧擴展時無法申請到足夠的內存時
虛擬機參數設置:-Xss
本地方法棧
與 Java 虛擬機棧發揮的作用類似,區別在于本地方法棧服務于 Native 方法。
可能出現的異常:與 Java 虛擬機棧一樣。
Java 堆
唯一目的:存放對象實例
垃圾回收器管理的內存區域
可以處于物理上不連續的內存空間中,但是在邏輯上應該是連續的
可能出現的異常:
OutOfMemoryError:Java 堆中沒有內存完成實例分配,并且堆也無法再擴展時
虛擬機參數設置:
最大值:-Xmx
最小值:-Xms
兩個參數設置成相同值時可避免堆自動擴展
方法區
用于存儲已被 Java 虛擬機加載的類型信息、常量、靜態變量、即時編譯器編譯后的代碼緩存等數據
可以選擇不實現垃圾收集,換句話說垃圾收集行為在這個區域非常少見,但是對于經常動態性生成大量 Class 的應用,如 Spring 等,需要特別注意類的回收情況
可能出現的異常:
OutOfMemoryError:方法區無法滿足新的內存分配需求時
運行時常量池
運行時常量池是方法區的一部分
Class 文件除了有類的版本、字段、方法、接口等描述信息外,還有一項是常量池表,用于存放編譯期生成的各種字面量(就是代碼中定義的 static final 常量)和符號引用,這部分內容將在類加載后存放到方法區的運行時常量池中
可能出現的異常:
OutOfMemoryError:常量池無法再申請到內存時
直接內存
不屬于 Java 虛擬機運行時數據區域的一部分,放到這里講是因為這部分內存在使用的時候也可能導致 OutOfMemoryError 異常的出現:各個內存區域總和大于物理內存限制(包括物理的和操作系統級的限制),從而導致動態擴展時出現 OutOfMemoryError 異常
JDK1.4 的 NIO 類可以使用 Native 函數庫直接分配堆外內存,然后通過一個存儲在 Java 堆里面的 DirectByteBuffer 對象作為這塊內存的引用進行操作,好處是避免了在 Java 堆和 Native 堆中來回復制數據,能在一些場景中提高性能
虛擬機參數設置:-XX:MaxDirectMemorySize
默認等于 Java 堆最大值,即-Xmx指定的值
HotSpot 虛擬機堆中的對象
介紹完 Java 虛擬機的運行時數據區域后,我們大致了解的 Java 虛擬機內存模型的概況。這一小節將介紹 HotSpot 虛擬機在 Java 堆中對象分配、布局和訪問的全過程。
對象的創建(new)
當虛擬機遇到 new 指令時:
檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,并檢查這個符號引用代表的類是否已經被加載、解析和初始化過。如果沒有,則先把這個類加載進內存
類加載檢查通過后,虛擬機將為這個新的對象分配內存,內存的大小在類加載完成后確定
在 Java 堆中為新對象分配可用內存
內存分配完成后,虛擬機將分配到的內存空間都初始化為零值
虛擬機設置對象頭中的數據
此時,從虛擬機的角度看,對象已經創建好了,但從 Java 程序角度看,對象創建才剛剛開始,構造函數還沒有執行
第 3 步中,為對象分配可用內存時,會涉及兩個問題:
內存分配方式
指針碰撞(Java 堆中的內存是絕對規整的)
所有被使用過的內存放在一邊,沒有被使用過的內存放在另一邊,中間放一個指針,作為分界點的指示器,那所分配的內存就僅僅是把那個指針向空閑內存空間方向挪一段與對象大小相等的距離
空閑列表(Java 堆中被使用的內存和空閑內存相互交錯在一起)
Java 虛擬機需要維護一個列表,記錄上哪些內存塊是可用的,在分配的時候從列表中找一塊大小足夠大的空間劃分給對象實例
在并發情況下虛擬機創建對象也并不是線程安全的,可能出現正在給對象 A 分配內存,指針還沒來得及修改,對象 B 又同時使用了原來的指針來分配內存的情況
對分配內存空間的動作進行同步處理( 采用 CAS 配上失敗重試的方式保證更新操作的原子性 )
把內存分配的動作按照線程劃分在不同的空間中進行(每個線程在 Java 堆中預先分配一塊小內存(這個小內存稱為本地線程分配緩沖區),哪個線程要分配內存,就在哪個線程的本地線程分配緩沖區中分配,只有這個本地線程分配緩沖區分配完了,分配新的本地線程分配緩沖區才需要同步鎖定)
虛擬機參數設置:-XX:+/-UseTLAB
對象的內存布局
在 HotSpot 虛擬機中,對象在堆內存中的存儲布局分為三部分:
對象頭(包括兩類信息)
用于存儲對象自身的運行時數據,如 HashCode、GC 分代年齡、鎖狀態標志、線程持有的鎖、偏向線程 ID、偏向時間戳等
類型指針,即對象指向它的類型元數據的指針,虛擬機通過這個指針來確定該對象是哪個類的實例。
實例數據(存儲我們在程序代碼中定義的各種類型的字段內容)
這部分數據受到虛擬機分配策略參數(-XX:FieldsAllocationStyle)和字段在代碼中定義順序的影響
對齊填充(沒有實際意義,起到占位符的作用)
對象的訪問定位
我們創建對象之后自然是要使用對象,Java 程序會通過棧上的 reference 數據來操作堆上的具體對象(reference 是一個指向對象的引用)。
主流的對象訪問方式有兩種:
通過句柄訪問(reference 中存儲的是穩定的句柄地址,在對象被移動的時候只會改變句柄中的實例數據指針,而 reference 本身不需要被修改)
通過直接指針訪問(速度快得一批,節省了一次指針定位的時間開銷)
本文小結
本文從概念上介紹了 Java 虛擬機內存的各個區域以及這些區域的作用、服務對象和其中可能出現的異常等,還介紹了虛擬機創建對象(new)的過程、對象的內存布局和如何訪問對象。
總結
以上是生活随笔為你收集整理的JAVA虚拟机 安全区域_Java虚拟机的内存区域的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 回国在即!旅美熊猫丫丫上海检疫后将被北京
- 下一篇: java中0l 1_Java基础笔记1