由"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太大了。
文章错误和不足之处,肯请批抨指正!