1.程式計數器:執行緒私有,用於儲存目前所執行的指令位置
2.Java虛擬機棧:線程私有,描敘Java方法執行模型;執行方法時都會建立一個棧幀,儲存局部變量,基本類型變量,引用等資訊
3.Java本地方法堆疊:執行緒私有,為虛擬機器使用到的Native方法服務
4.Java堆:執行緒共享,是垃圾收集器的主要工作地方;儲存物件實例等
5.方法區:執行緒共享;儲存類別訊息,常數,靜態變數等
運行時常數:存放編譯時產生的各種字面量和符號參考
6.直接記憶體:機器的記憶體
先檢查常量池能否定位到此類的符號引用,並檢查類別是否已經載入初始化,否則要先執行載入過程;
為物件分配記憶體:計算空間並從堆中劃分一塊連續或不連續的區域;使用的是cas+失敗重試,避免線程安全問題(因為物件創建十分頻繁,不知道當前記憶體有沒有被分配出去)
初始化記憶體空間:將分配的記憶體空間初始化0值
設定物件基本資訊:元資料、hash碼、gc等
執行java的init初始化:
物件頭:儲存物件的hash碼、鎖定狀態等和類型指標(物件所指向類別的元資料)
實例資料:物件真正儲存的資訊
對齊填入:填入符合規則
3.物件的存取物件的存取物件的存取物件的存取物件的存取物件。 ,透過java棧上的reference數據,它維護了一個指向物件的引用
存取方式:句柄和直接存取
#
## # 句柄:堆中維護句柄池,reference指向句柄,句柄中包含了物件實例資料和類型資料的位址資訊 移動一次指標定位 # 直接:reference直接指向物件位址 直接:reference直接指向物件位址 問題 總速 1 .java堆溢位的是物件:可以建立大量物件來實現堆疊溢位: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.方法區和常數池溢位
在JDK1.6前,可以透過建立大量的String,虛擬機會複製物件放入常數池,從而溢出
在1.7及以後,不可以這樣,因為虛擬機器只會在常數池中保存首次出現此物件時物件的參考 方法區的溢出:方法區保存的是類別的信息,透過產生大量的動態類別來溢出,如spring其實也是透過動態代理產生的類別
public class JavaMethodAreaOOM{public static void main(String[]args){while(true){//创建大量的动态类,动态代理OOMObjectEnhancer 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及以後保存的是首次出現的引用;理解上面的分析
參數:-XX:MaxDirectMemorySize直接記憶體大小;預設==最大堆疊記憶體
## ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### #####
以上是java記憶體區域與記憶體溢出異常的詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!