運行時數據區域:
根據 JVM 規范,JVM 內存共分為虛擬機棧、堆、方法區、程序計數器、本地方法棧五個部分。
程序計數器(線程私有):
是當前線程所執行的字節碼的行號指示器,每條線程都要有一個獨立的程序計數器,這類內存也稱為“線程私有”的內存。
正在執行java方法的話,計數器記錄的是虛擬機字節碼指令的地址(當前指令的地址)。如果還是Native方法,則為空。
這個內存區域是唯一一個在虛擬機中沒有規定任何OutOfMemoryError情況的區域。
Java虛擬機棧(線程私有):
也是線程私有的。
每個方法在執行的時候會創建一個棧幀,存儲了局部變量表,操作數棧,動態連接,方法返回地址等。
每個方法從調用到執行完畢,對應一個棧幀在虛擬機棧中的入棧和出棧。
通常所說的棧,一般是指虛擬機棧中的局部變量表部分。
局部變量表所需的內存在編譯期間完成分配。
如果線程請求的棧深度大於虛擬機所允許的深度,則StackOverflowError。
如果虛擬機棧可以動態擴展,擴展到無法申請足夠的內存,則OutOfMemoryError。
本地方法棧(線程私有):
和虛擬機棧類似,主要為虛擬機使用到的Native方法服務。
也會拋出StackOverflowError和OutOfMemoryError。
Java堆(線程共享):
被所有線程共享的一塊內存區域,在虛擬機啟動時創建,用於存放對象實例。
堆可以按照可擴展來實現(通過-Xmx和-Xms來控制)
當堆中沒有內存可以分配給實例,也無法再擴展時,則拋出OutOfMemoryError異常。
方法區(線程共享):
被所有線程共享的一塊內存區域。
用於存儲已被虛擬機加載的類信息,常量,靜態變量等。
這個區域的內存回收目標主要針對常量池的回收和對類型的卸載。
當方法區無法滿足內存分配需求時,則拋出OutOfMemoryError異常。
在HotSpot虛擬機中,用永久代來實現方法區,將GC分代收集擴展至方法區,但是這樣容易遇到內存溢出的問題。
JDK1.7中,已經把放在永久代的字符串常量池移到堆中。
JDK1.8撤銷永久代,引入元空間。
運行時常量池:
是方法區的一部分,用於存放編譯期生成的各種字面量和符號引用。
當常量池無法再申請到內存時,則拋出OutOfMemoryError異常。
直接內存:
不是運行時數據區的一部分,但也可能拋出OutOfMemoryError異常。
在JDK1.4中新加入的NOI類,引入了一種基於通道與緩沖區的I/O方式,它可以使用Native函數直接分配堆外內存,
然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。