首頁  >  文章  >  Java  >  java記憶體區域與記憶體溢出異常的詳細介紹

java記憶體區域與記憶體溢出異常的詳細介紹

零下一度
零下一度原創
2017-06-25 10:58:351427瀏覽

java記憶體區域與記憶體溢出例外

一、執行階段資料區

  

 

  1.程式計數器:執行緒私有,用於儲存目前所執行的指令位置

  2.Java虛擬機棧:線程私有,描敘Java方法執行模型;執行方法時都會建立一個棧幀,儲存局部變量,基本類型變量,引用等資訊

  3.Java本地方法堆疊:執行緒私有,為虛擬機器使用到的Native方法服務

  4.Java堆:執行緒共享,是垃圾收集器的主要工作地方;儲存物件實例等

  5.方法區:執行緒共享;儲存類別訊息,常數,靜態變數等

    運行時常數:存放編譯時產生的各種字面量和符號參考

  6.直接記憶體:機器的記憶體

二、虛擬機器物件

  1.物件的建立

  • 先檢查常量池能否定位到此類的符號引用,並檢查類別是否已經載入初始化,否則要先執行載入過程;

  • 為物件分配記憶體:計算空間並從堆中劃分一塊連續或不連續的區域;使用的是cas+失敗重試,避免線程安全問題(因為物件創建十分頻繁,不知道當前記憶體有沒有被分配出去)

  • 初始化記憶體空間:將分配的記憶體空間初始化0值

  • 設定物件基本資訊:元資料、hash碼、gc等

  • 執行java的init初始化:

  2.物件的記憶體佈局

    物件頭:儲存物件的hash碼、鎖定狀態等和類型指標(物件所指向類別的元資料)

    實例資料:物件真正儲存的資訊

    對齊填入:填入符合規則

  3.物件的存取權的存取物件的存取物件:填入符合規則

  3.物件的存取物件的存取物件的存取物件的存取物件的存取物件。 ,透過java棧上的reference數據,它維護了一個指向物件的引用

    存取方式:句柄和直接存取

#      

##      #      句柄:堆中維護句柄池,reference指向句柄,句柄中包含了物件實例資料和類型資料的位址資訊

        移動一次指標定位

     

#      直接:reference直接指向物件位址

    直接:reference直接指向物件位址

    問題   總速 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){//创建大量的动态类,动态代理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及以後保存的是首次出現的引用;理解上面的分析

#   4.本機直接記憶體

    參數:-XX:MaxDirectMemorySize直接記憶體大小;預設==最大堆疊記憶體

 

## ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### ##### 

以上是java記憶體區域與記憶體溢出異常的詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn