代碼中開了幾個線程,遇到"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太大了。
文章錯誤和不足之處,肯請批抨指正!