深入瞭解Java虛擬機器(1)java記憶體地區與記憶體溢出異常

來源:互聯網
上載者:User

標籤: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記憶體地區與記憶體溢出異常

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.