由"java.lang.OutOfMemoryError: unable to create new native thread"說起

代碼中開了幾個線程,遇到"java.lang.OutOfMemoryError: unable to create new native thread"異常。經研究,主要原因是JVM -Xss值過大導致。


計算java程序最大可開線程數的公式:

最大可建線程數= (進程用戶可用空間 - JVM堆大小-JVM持久代大小-Native Heap大小)/ java線程棧大小


解釋公式:


一.JVM中的數據



各部分說明:

1. 程序計數器
  線程私有
  當前線程所執行的字節碼的行號指示器
2. 虛擬機棧
  線程私有,和程序計數器一樣,都屬於線程私有,生命週期與線程相同。
  存:Java方法(局部變量表(基本數據類型)、操作數棧、動態鏈棧、方法出口)
3. 本地方法棧
  線程私有
  存:Native方法,與虛擬機棧相似
4. 堆 
  線程共享
  存:對象實例,新生代 老年代
  堆大小設置
  -Xmx 設置JVM最大可用內存
  -Xms 設置JVM初始堆大小
  -Xmn 設置新生代大小。持久代一般固定大小爲64m,所以增大新生代後,將會減小老年代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。
  -Xss  設置每個線程的堆棧大小
5. 方法區
  線程共享 Non-Heap 非堆 熟稱“持久代”
  存:已被加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據
  -XX:MaxPermSize=n:  設置持久代大小

總結:整個JVM佔用存儲空間 = JVM堆大小 + 持久代大小。


二.系統進程中的數據:


java編程語言是平臺無關的,但java代碼終究還得依靠具體平臺來實現。以Windows32位系統爲例:


圖1:java代碼調用順序

在Windows平臺下,java程序會通過windows進程來實現。Win32下進程地址空間爲4G,其中2G被操作系統佔用,用戶可用空間爲2G。JVM也是Win32程序,它裏面所包含內容都要被加載入進程用戶地址空間。此處JAVA支持JNI技術,能夠直接調用底層庫中方法(native method)。C語言庫方法運行時需要的存儲空間來自於C heap(C heap是由系統從用戶地址空間創建的),爲了滿足JNI調用,大概要從C heap上獲取128M空間。我把這部分從C heap獲取的空間叫做Native heap。


三.公式計算


1.進程用戶可用空間:根據自己操作系統得出,我是32位Windows,所以該值爲2G。

2.JVM堆大小:默認是用戶可用空間的1/4。eclispe 中window->preferences->Java->Installed JRE ,點擊右側的Edit 按鈕,在編輯界面中的 “Default VM Arguments ”選項。我的是512M。

注:不要看eclipse.ini中的參數,那是eclipse自身運行用的。

3.JVM持久帶大小:默認是用戶可用空間的1/4,按默認配置我機器上是512M。

4.Native Heap大小:資料不全,大概是128M。

5.java線程棧大小:java創建線程時分配的存儲空間以供線程運行。java線程是最終依賴底層代碼實現的,線程棧要從系統的C heap上獲取空間。"進程用戶可用空間 - JVM堆大小-JVM持久代大小-Native Heap大小"就是C heap可以擴展到的最大尺寸。java線程棧越小,可創建線程數越多。我eclipse Default VM Arguments中線程棧高達64M. 

算一下我java程序中能建多少個線程

(2048-512-512-128)/64 = 14

我實際運行了下最多能建9個線程,因爲有的存儲空間消耗不好預估。主要是-Xss太大了。


文章錯誤和不足之處,肯請批抨指正!

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