1. 程式人生 > >Java堆溢位及棧溢位

Java堆溢位及棧溢位

記憶體溢位Out Of Memory(OOM):指申請記憶體時,沒有足夠的記憶體供其使用。

記憶體洩露Memory Leak:記憶體洩露,程式申請記憶體後,無法釋放已申請的記憶體空間。記憶體洩露的堆積,浪費了記憶體空間,可能會造成OOM.

堆溢位資訊: OutOfMemoryError : Java heap space

分析工具:Eclipse Memory Analyzer

棧溢位

HotSpot VM並不區分虛擬機器棧和本地方法棧,所以 -Xoss引數(設定本地方法棧大小)在HotSpot無效,只能通過-Xss引數設定棧容量。

  1. 如果執行緒請求的棧深度大於虛擬機器所允許的最大深度,將丟擲StackOverflowError異常。
  2. 如果虛擬機器在擴充套件棧時無法申請到足夠的空間,丟擲OutOfMemoryError異常。

本質上是同一件事。

經過驗證——在單執行緒的情況下,無論是棧幀太大還是虛擬機器容量太小,當記憶體無法分配是,丟擲的都是StackOverflowError異常。

分配給棧的記憶體並不是越大越好,因為棧記憶體越大,執行緒多,留給堆的空間就不多了,容易丟擲OOM。JVM的預設引數一般情況沒有問題(包括遞迴)。

方法區和執行時常量池溢位

執行時常量池是方法區的一部分。

String.intern()是Native方法。

    1.8實測
        String sb1 = new StringBuilder("計算機").append("軟體").toString();
        System.out.println(sb1.intern() == sb1);

        String string = new StringBuilder("Ja").append("va").toString();
        System.out.println(string.intern() == string);
        
        true
        false

1.6之前執行intern()會把首次遇到的字串例項複製到永久代,返回的也是永久代這個例項的引用,而StringBuilder例項在Java堆上,必然不是同一個引用,將返回false。

1.7之後intern()不會再複製例項,只是在常量池中記錄首次出現的例項引用,因此intern返回的引用和由StringBuilder建立的那個字元例項是同一個。由於java這個字串常量池已經有了,所以intern返回的是常量池的引用,與string並不相等。

方法區溢位:常見的記憶體溢位,一個類要被垃圾收集器回收掉,條件比較苛刻。在經常動態生成大量Class的應用中,需要特別注意。

本機記憶體直接溢位

-XX:MaxDirectMemorySize 指定DirectMemory,如果不指定,預設與Java最大值(-Xmx)一樣。 由DirectMemory導致的記憶體溢位,Heap Dump檔案不會看到明顯的異常,如果OOM之後Dump檔案很小,而程式又直接使用了NIO,就要考慮是否是Direct Memory溢位了。