標籤:style 基本類型 過程 安全 sys 連續 收集器 直接記憶體 對象
java記憶體地區與記憶體溢出異常一、運行時資料區域
1.程式計數器:線程私人,用於儲存當前所執行的指令位置
2.Java虛擬機器棧:線程私人,描敘Java方法執行模型;執行方法時都會建立一個棧幀,儲存局部變數,基本類型變數,引用等資訊
3.Java本地方法棧:線程私人,為虛擬機器使用到的Native方法服務
4.Java堆:線程共用,是垃圾收集器的主要工作地方;儲存物件執行個體等
5.方法區:線程共用;儲存類資訊,常量,靜態變數等
運行時常量:存放編譯時間產生的各種字面量和符號引用
6.直接記憶體:機器的記憶體
二、虛擬機器對象 1.對象的建立
- 先檢查常量池能否定位到此類的符號引用,並檢查類是否已經載入初始化,否則要先執行載入過程;
- 為對象分配記憶體:計算空間並從堆中劃分一塊連續或不連續的地區;使用的是cas+失敗重試,避免安全執行緒問題(因為對象建立十分頻繁,不知道當前記憶體有沒有被分配出去)
- 初始化記憶體空間:將分配的記憶體空間初始化0值
- 設定對象基本資料:中繼資料、hash碼、gc等
- 執行java的init初始化:
2.對象的記憶體布局
對象頭:儲存物件的hash碼、鎖狀態等 和 類型指標(對象所指向類的中繼資料)
執行個體資料:對象真正儲存的資訊
對齊填充:填充符合規則
3.對象的訪問定位
對象的訪問,通過java棧上的reference資料,它維護了一個指向對象的引用
訪問方式:控制代碼和直接存取
控制代碼:堆中維護控制代碼池,reference指向控制代碼,控制代碼中包含了對象執行個體資料和類型資料的地址資訊
移動方便,直接修改控制代碼中的執行個體資料即可;開銷大,多了一次指標定位
直接:reference直接指向對象地址
速度快
三、實戰OutofMemoryERROR 1.java堆溢出
參數:-Xms堆最小值;-Xmx堆最大值;-XX:+HeapDumpOnOutOfMemoryError出現溢出時記憶體快照分析
堆中存放的是對象:可以建立大量對象來實現堆溢出:heap space
2.棧溢出
參數:-Xss設定棧值
棧深度,可以通過無限遞迴增加棧深度、或建立大量線程實現
//遞迴來StackOverFlowerpublic class JavaVMStackSOF { private int stackLength = 1; public void stackLeak(){ stackLength++; stackLeak(); } public static void main(String[] args)throws Throwable{ JavaVMStackSOF oom = new JavaVMStackSOF(); try { oom.stackLeak(); } catch(Throwable e){ System.out.println("stack length:" + oom.stackLength); throw e; } }} 3.方法區和常量池溢出
參數:-XX:PermSize方法區大小;-XX:MaxPermSize方法區最大大小
在JDK1.6前,可以通過建立大量的String,虛擬機器會複製對象放入常量池,從而溢出
在1.7及以後,不可以這樣,因為虛擬機器只會在常量池中儲存首次出現此對象時對象的引用
方法區的溢出:方法區儲存的是類的資訊,通過產生大量的動態類來溢出,如spring其實也是通過動態代理產生的類
public class JavaMethodAreaOOM{ public static void main(String[]args){ while(true){//建立大量的動態類,動態代理OOMObject Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor(){ public Object intercept(Object obj,Method method,Object[]args,MethodProxy proxy)throws Throwable{ return proxy.invokeSuper(obj,args); }} ); enhancer.create(); }} static class OOMObject{ }}
String.intern()是一個Native方法,它的作用是:如果字串常量池中已經包含一個等於此String對象的字串,則返回代表池中這個字串的String對象;否則,將此String對象包含的字串添加到常量池中,並且返回此String對象的引用
JDK6及以前:方法區(永久代)是單獨的,常量池在方法區內
JDK7:去永久代
public class RuntimeConstantPoolOOM{ public static void main(String[]args){ String str1=new StringBuilder("電腦").append("軟體").toString(); System.out.println(str1.intern()==str1); String str2=new StringBuilder("ja").append("va").toString(); System.out.println(str2.intern()==str2); }}
這段代碼在JDK 1.6中運行,會得到兩個false,而在JDK 1.7中運行,會得到一個true和一個false。
產生差異的原因是:在JDK 1.6中,intern()方法會把首次遇到的字串執行個體複製到永久代中,返回的也是永久代中這個字串執行個體的引用,而由StringBuilder建立的字串執行個體在Java堆上,所以必然不是同一個引用,將返回false。
而JDK 1.7:intern()實現不會再複製執行個體,只是在常量池中記錄首次出現的執行個體引用,因此intern()返回的引用和由StringBuilder建立的那個字串執行個體是同一個。
對str2比較返回false是因為“java”這個字串在執行StringBuilder.toString()之前已經出現過,字串常量池中已經有它的引用了,不符合“首次出現”的原則,而“電腦軟體”這個字串則是首次出現的,因此返回true
注意:1.7及以後儲存的是首次出現的引用;理解上面的分析
4.本機直接記憶體
參數:-XX:MaxDirectMemorySize直接記憶體大小;預設==最大堆記憶體
深入瞭解Java虛擬機器(1)java記憶體地區與記憶體溢出異常