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虛擬機)]