由"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太大了。


文章错误和不足之处,肯请批抨指正!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值