一:運行時數據區域
1:程序計數器-線程私有在任何一個確定的時刻,一個處理器都只會執行一條線程中的指令。因此,爲了線程切換後能恢復到正確的執行位置,每條線程都需要有一個獨立的程序計數器,稱爲“線程私有”的內存,是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。
2: Java虛擬機棧(常說的棧)-線程私有
虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出口等。
局部變量表:基本數據類型,對象引用、returnAddress類型。long 和double佔據兩個局部變量空間,其餘只有一個。
如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常;如果虛擬機棧可以動態擴展,如果擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。
3:本地方法棧
本地方法棧則爲虛擬機使用到的Native方法服務。
4:堆-線程共享
所有的對象實例以及數組都要在堆上分配。
5:方法區
用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
二:對像創建
1:虛擬機在遇到一條new 指令時,首先檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,並檢查這個符號引用代表的類是否已經被加載、解析和初始化過。如果沒有,那必須先執行相應的類加載過程。
2:分配內存
指針碰撞:所用用過的內存放在一邊,沒用過的放在另一邊
空閒列表:用過的和沒用過的放在一起
避免內存分配衝突-本地線程分配緩衝(每個線程預先分配一塊內存)
3:將分配的內存空間初始化爲零值
4:虛擬機對對象進行必要的設置。調用init方法
三:對象的內存佈局
對象頭:
兩部分信息:第一部分用於存儲對象自身的運行時數據(32bit 或64bit),另一部分是類型指針,即對象指向它的類元數據的指針。如果對象是Java數組,那在對象頭中必須有一塊用於記錄數組長度的數據,因爲虛擬機可以通過普通Java對象的元數據信息確定Java對象的大小。
實例數據
對齊填充
四:訪問對象
1:通過句柄訪問對象
2:通過指針直接訪問
五:OutOfMemoryError異常
1:Java 堆溢出
通過設置參數-Xms20m(設置堆得最小值) -Xmx20m(堆的最大值) -XX:+HeapDumpOnOutOfMemoryError可以讓虛擬機出現溢出異常時Dump出當前的內存堆轉儲快照。
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
// TODO Auto-generated method stub
List<OOMObject> list = new ArrayList<HeapOOM.OOMObject>();
while (true) {
list.add(new OOMObject());
}
}
}
運行結果:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid5213.hprof ...
Heap dump file created [27617151 bytes in 0.167 secs]
2:虛擬基棧和本地方法棧溢出(通過調整-Xss參數設置-Xss128k)
如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverflowError異常
如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常
3:方法區和運行時常量池異常
通過-XX:PermSize和-XX:MaxPermSize限制方法區大小
String.intern()是一個Native方法,作用是:如果字符串常量池中已經包含一個等於此String對象的字符串,則返回代表池中這個字符串的String對象;否則,將此String對象包含的字符串添加到常量池中,並且返回此String對象的一個引用。
如果異常,拋出PermGen space