1.程序計數器:線程私有,用於存儲當前所執行的指令位置java
2.Java虛擬機棧:線程私有,描敘Java方法執行模型;執行方法時都會建立一個棧幀,存儲局部變量,基本類型變量,引用等信息spring
3.Java本地方法棧:線程私有,爲虛擬機使用到的Native方法服務安全
4.Java堆:線程共享,是垃圾收集器的主要工做地方;存儲對象實例等app
5.方法區:線程共享;存儲類信息,常量,靜態變量等佈局
運行時常量:存放編譯時生成的各類字面量和符號引用ui
6.直接內存:機器的內存spa
對象頭:存儲對象的hash碼、鎖狀態等 和 類型指針(對象所指向類的元數據)線程
實例數據:對象真正存儲的信息3d
對齊填充:填充符合規則代理
對象的訪問,經過java棧上的reference數據,它維護了一個指向對象的引用
訪問方式:句柄和直接訪問
句柄:堆中維護句柄池,reference指向句柄,句柄中包含了對象實例數據和類型數據的地址信息
移動方便,直接修改句柄中的實例數據便可;開銷大,多了一次指針定位
直接:reference直接指向對象地址
速度快
參數:-Xms堆最小值;-Xmx堆最大值;-XX:+HeapDumpOnOutOfMemoryError出現溢出時內存快照分析
堆中存放的是對象:能夠建立大量對象來實現堆溢出:heap space
參數:-Xss設置棧值
棧深度,能夠經過無限遞歸增長棧深度、或建立大量線程實現
//遞歸來StackOverFlower public 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; } } }
參數:-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及之後保存的是首次出現的引用;理解上面的分析
參數:-XX:MaxDirectMemorySize直接內存大小;默認==最大堆內存