詳談jvm線程棧空間內存分配位置

jvm線程棧空間內存分配位置

jvm的線程棧申請的內存空間屬於堆外內存,是向操作系統申請的,也不是JVM直接內存,雖然類似。

JVM能創建的線程數需要的內存,不是JVM運行內存,堆內存,直接內存,而是操作系統剩餘的可用內存,這個也決定瞭能創建的線程數,如果內存不夠用,創建線程的時候便會出現內存溢出的錯誤。

在操作系統的可用內存不足的情況下,想要創建更多的線程,可以考慮減少線程棧的空間大小(-Xss),但是不建議過小,棧嘗試減小容易棧溢出錯誤。

————————–分割線————————–

後來有次早上5點睡不著,起床做瞭個測試驗證,在這裡補充下這個測試說明,當時隻是發瞭個朋友圈,過得有點久瞭現在我也隻從手機上找到幾張圖片貼上來:

JVM配置如下

設置最大堆10m,線程棧大小10m,直接內存5m。

創建1000條線程,調用10w次函數(調用次數再過多,棧深不夠就溢出瞭),瘋狂壓棧但不出棧(遞歸調用)。然後看內存,物理內存占用7g多,有次是5g多。沒有堆溢出,也沒有直接內存溢出。說明線程棧內存在堆外分配,不屬於堆內存(線程對象還是分配在堆內存空間),也不屬於直接內存部分。

測試截的一些圖片如下

JVM配置

main方法創建1條線程並運行:

main方法創建1000條線程並運行:

測試代碼

/**
 * @Auther: 許曉東
 * @Date: 19-8-3 05:14
 * @Description:
 */
public class ThreadStackTest {
    static Map<Integer, Thread> map = new HashMap<>();
    static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                recursive(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
 
    private static void recursive(int num) throws InterruptedException {
        if (num == 20000) {
            Thread.sleep(Integer.MAX_VALUE);
        }else {
            recursive(++num);
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        System.gc();
        int nums = 1000;
        //int nums = 1;
        for (int i = 0; i < nums; i++) {
            map.put(i, new Thread(runnable));
        }
 
        for (Thread thread :
                map.values()) {
            thread.start();
        }
 
        System.out.println(String.format("創建%d條線程後gc情況:", nums));
        System.gc();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

jvm棧大小設置

1、棧內存大小設置

棧內存為線程私有的空間,每個線程都會創建私有的棧內存。棧空間內存設置過大,創建線程數量較多時會出現棧內存溢出StackOverflowError。同時,棧內存也決定方法調用的深度,棧內存過小則會導致方法調用的深度較小,如遞歸調用的次數較少。

-Xss:如-Xss128k

通常隻有幾百K

決定瞭函數調用的深度

每個線程都有獨立的棧空間

局部變量、參數 分配在棧上

2、遞歸調用

package com.thread.study; 
public class Stack {
  private static int count=0;
  public static void recursion(long a,long b,long c){
   long e=1,f=2,g=3,h=4,i=5,k=6,q=7,x=8,y=9,z=10;
   count++;
   recursion(a,b,c);
  }
  public static void main(String args[]){
   try{
    recursion(0L,0L,0L);
   }catch(Throwable e){
    System.out.println("deep of calling = "+count);
    e.printStackTrace();
   }
  } 
}
  • -Xss128k:deep of calling = 306
  • -Xss256k:deep of calling = 761

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: