StackOverflowError的分析和理解


1. 在java虛擬機規范中,定義了在虛擬機棧和本地方法棧中會產生StackOverflowError

2. 虛擬機棧和本地方法棧一般就是我們說的java內存管理中的棧

3. 虛擬機棧和本地方法棧是線程之間的獨立內存,每一個線程在創建時,java虛擬機都會給該線程分配一塊獨立的內存區域,一般將此內存區域划分為虛擬機棧,本地方法棧,程序計數器

4. 虛擬機棧中存儲了方法執行時相關信息,每個方法在調用時都會在虛擬機棧中創建一個方法幀,方法幀中包含了局部變量,操作數,動態鏈接,方法出口等信息

5. 本地方法棧和虛擬機棧基本相同,不同的是本地方法棧是針對線程中的native方法

6. 程序計數器包含了一個程序執行指針,指向了字節碼當前執行的行數

7. 在java虛擬機規范中,虛擬機棧和本地方法棧都會出現StackOverflowError和OutofMemoryError,程序計數器是java虛擬機中唯一一塊不會產生error的內存區域

8. StackOverflowError代表的是,當棧深度超過虛擬機分配給線程的棧大小時就會出現此error

9. OutofMemoryError代表的是,當再申請新的內存時,虛擬機分配給線程的內存大小中無法再分配新的內存,就會出現此error

10. -Xss1024M虛擬機參數可以設置虛擬機分配給每個線程的內存大小,程序計數器占很小的內存(可以忽略),一般此內存和線程棧內存相等

11. 在HotSpot虛擬機中,是將虛擬機棧和本地方法棧合二為一的


 

以下測試結構都是在HotSpot虛擬機中進行的:

1. 在單線程操作中,無論是棧深度無限增加,還是棧幀(每個方法調用執行時都會在棧中創建一個棧幀,用來存儲局部變量,操作數棧,動態鏈表,方法出口等信息)占的空間太大,都出現的是StackOverflowError

2. 不斷創建新的線程的實踐中會出現OutofMemoryError的錯誤

測試一:無限增加棧深度(使用了-Xss1024M參數指定了線程棧大小為1G)

 1 public class StackOverflowErrorMain {
 2     int stackLength = 0;
 3 
 4     public StackOverflowErrorMain() {
 5     }
 6 
 7     public void addStackLength(){
 8         stackLength++;
 9         addStackLength();
10     }
11 
12     public static void main(String[] args){
13         StackOverflowErrorMain sofem = new StackOverflowErrorMain();
14         try {
15             sofem.addStackLength();
16         }catch (Throwable e){
17             System.out.println(sofem.stackLength);
18             e.printStackTrace();
19         }
20     }
21 }

 輸出:

66961075
java.lang.StackOverflowError
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
    at com.mrlu.stackError.StackOverflowErrorMain.addStackLength(StackOverflowErrorMain.java:14)
...

 

測試二:棧幀無限擴大(HotSpot 64位虛擬機要求最小棧大小為160K)

 

測試三:不斷創建新的線程:

public class StackOverflowErrorMain {
    int threadCount = 0;

    public StackOverflowErrorMain() {
    }

    public void addNewThread(){
        while (true) {
            threadCount++; //線程的數量
            new Thread() {
                @Override
                public void run() {
                    while (true) ; //線程執行不能停
                }
            }.start();
        }
    }

    public static void main(String[] args){
        StackOverflowErrorMain sofem = new StackOverflowErrorMain();
        try {
            sofem.addNewThread();
        }catch (Throwable e){
            System.out.println(sofem.threadCount);
            e.printStackTrace();
        }
    }
}

 

輸出:

 

不斷創建新線程產生OutofMemoryError的問題總結:

  操作系統對每個進程分配的內存有大小限制(windows64位系統中最大是2G內存),對於java虛擬機,最大是2G的內存,不算java虛擬機進程啟動時占的內存,剩下的內存都分配給了共享區和線程獨享區,也就是堆內存+方法區內存+線程棧內存(程序計數器忽略不及),那么不論堆內存和方法區內存占的內存空間有多小,線程棧內存的大小總是有限制的,由於java虛擬機會為每個線程分配一塊內存,如果線程數量足夠多,當再申請新的內存時,發現無內存可用了,就會拋出OutofMemoryError


 

問題:

1. -Xss1024M代表的是每個線程在創建啟動時都會分配這個固定大小的內存嗎,還是說這個內存是這個線程棧內存的最大值?

2. linux中每個進程內存大小的限制是多少?

3. 如何實時查看一個進程占內存的大小?(top?)

4. 如何實時查看jvm虛擬機中每個線程占內存的大小?


 

參考書籍:[(深入理解JAVA虛擬機)]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM