Java JVM 內存溢出Oom

Java 虛擬機的默認內存分配:

-Xms 設置初始化默認內存,初始默認爲物理內存的1/64

-Xmx最大分配內存,初始爲物理內存的1/4

驗證:

本機的實際內存爲16G:

public class Test {
    public static void main(String[] args) {
        long maxMemory = Runtime.getRuntime().maxMemory();//1/4
        long totalMemory = Runtime.getRuntime().totalMemory();//1/64
        System.out.println("-Xmx:Max_Memory = " + maxMemory+"字節"+(maxMemory/(double)1024/1024)+"MB");
        System.out.println("-Xms:Total_Memory = " + totalMemory+"字節"+(totalMemory/(double)1024/1024)+"MB");

    }
}

-Xmx:Max_Memory = 3799515136字節3623.5MB
-Xms:Total_Memory = 257425408字節245.5MB

對JVM的內存進行調整時,必須將Max_Memory和Total_Memory調爲一致,避免運行時GC和應用程序運行時爭搶內存,理論值峯值和峯谷忽高忽低。

idea下的Jvm參數設置:

實現Max和Total都是1024M

Vm參數: -Xms1024m-Xmx1024m-XX:+PrintGCDetails

在這裏插入圖片描述
配置完再次運行上面的代碼:
在這裏插入圖片描述
得到驗證:

實現OutOfMemoryErro異常

在java虛擬機規範描述中,除了程序計數器外,虛擬機的幾個運行時區域都有可能發生OutOfMemoryErro異常的可能性。

Java堆溢出

驗證:

首先將JVM的內存改小使之比較容易的實現溢出:
在這裏插入圖片描述
然後new一個大於內存的數組:

 byte []bytes = new byte[1024 * 1024* 40];

得到異常:
在這裏插入圖片描述

虛擬機棧和本地方法棧的溢出

由於HotSpot虛擬機中並不區分虛擬機棧和本地方法棧,雖然-Xoss參數存在(設置本地方法棧的大小),但是實際時無效的,棧容量只能由-Xss參數設定,關於這兩塊區域,Java虛擬機規範中描述了兩種異常:

  • 如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出Stackoverflow Erro的異常。
  • 如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutofMemory Error

在單線程下,無論是由於棧幀太大還是虛擬機容量太小,當內存無法分配的時候:虛擬機都拋出的時StackOverflow Error 異常。

驗證:

public class Test {
    private int stackLength = 1;
    public void stackLeak(){
        stackLength++;
        stackLeak();

    }
    public static void main(String[] args) throws Throwable{
        Test test = new Test();
        try{
            test.stackLeak();
        }catch(Throwable e){
            System.out.println("stack length:"+ test.stackLength);
            throw e;
        }
    }
}

在這裏插入圖片描述
如果測試時不限於單線程,通過不斷地建立線程的方式倒是可以產生內存溢出異常,但是這樣的溢出異常與棧空間是否足夠大並不存在任何聯繫.
原因:操作系統分給每個進程的內存師有限的,虛擬機提供參數來控制Java堆和方法區的這裏兩部分的內存的最大值。每個線程分到的棧容量越大,可以建立的線程數量自然就越少,建立線程時越容易把內存耗盡。
如果使用虛擬機默認參數,棧深度在大多數情況下達到1000~2000是沒有問題的,對於常用的方法調用,這個深度完全夠了,但是如果建立過多的線程導致內存溢出,只能減少最大堆和減小棧容量來換取更多的線程。

方法區和運行時常量池的溢出

在jdk 1.6之前:
運行時常量池時方法區的一部分,所以我們用
-XX:PermSize 和 -XX:MaxPermSize限制方法區大小,從而間接限制其中常量池的容量
List list = new ArrayList();
int i = 0;
while(true){
list.add(String.valueOf(i++).intern());
}
我們會得到
OutofMemoryError : PemGen space
的異常
而在1.7中不會發生異常
因爲什麼呢:
再寫一個實例:

        String str1 = new StringBuilder("hello").append("world").toString();
        System.out.println(str1.intern() == str1);
        String str2 = new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern() == str2);


在java1.6中會輸出兩個false,而在之後的1.7,1.8裏會輸出一個true一個false。
因爲在JDK1.6中,intern()方法會把首次遇到的字符串實例複製到永久代中,返回的是這個字符串的引用,而由StringBuilder創建的字符串實例在Java堆中,所以不是同一個引用,返回false.
在JDK1.7中intern()不會再複製實例,只是在常量池中記錄首次出現的實例引用,因此intern()返回的引用和StringBuilder創建的那個字符串是同一個。對str2比較返回false是因爲這個字符串在執行StringBuilder.toString()之前已經出現過,字符串常量池中已經有他的引用了,不符合首次出現這個條件,所以返回false。


方法區用於存放Class的相關消息,如類名、訪問修飾符,常量池,字段描述,方法描述。
想讓他溢出就要產生大量的類去填滿方法區。
不再驗證。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章