【Java内存溢出】系列(5/8): Unable to create new native thread

本篇是Java内存溢出系列第5小篇。

1.OutOfMemoryError之Java heap space

2.OutOfMemoryError之GC overhead limit exceeded

3.OutOfMemoryError之Permgen space

4.OutOfMemoryError之Metaspace

5.OutOfMemoryError之Unable to create new native thread

6.OutOfMemoryError之Out of swap space?

7.OutOfMemoryError之Requested array size exceeds VM limit

8.OutOfMemoryError之Kill process or sacrifice child

Java程序本质上是多线程的, 可以同时执行多项任务。 类似于在播放视频的时候, 可以拖放窗口中的内容, 却不需要暂停视频播放, 即便是物理机上只有一个CPU。

线程(thread)可以看作是干活的工人(workers)。 如果只有一个工人, 在同一时间就只能执行一项任务. 假若有很多工人, 那么就可以同时执行多项任务。

和现实世界类似, JVM中的线程也需要内存空间来执行自己的任务. 如果线程数量太多, 就会引入新的问题:

java.lang.OutOfMemoryError: Unable to create new native thread 错误表达的意思是: 程序创建的线程数量已达到上限值

原因分析

JVM向操作系统申请创建新的 native thread(原生线程)时, 就有可能会碰到 java.lang.OutOfMemoryError: Unable to create new native thread 错误. 如果底层操作系统创建新的 native thread 失败, JVM就会抛出相应的OutOfMemoryError. 原生线程的数量受到具体环境的限制, 通过一些测试用例可以找出这些限制, 请参考下文的示例. 但总体来说, 导致 java.lang.OutOfMemoryError: Unable to create new native thread 错误的场景大多经历以下这些阶段:

  1. Java程序向JVM请求创建一个新的Java线程;
  2. JVM本地代码(native code)代理该请求, 尝试创建一个操作系统级别的 native thread(原生线程);
  3. 操作系统尝试创建一个新的native thread, 需要同时分配一些内存给该线程;
  4. 如果操作系统的虚拟内存已耗尽, 或者是受到32位进程的地址空间限制(约2-4GB), OS就会拒绝本地内存分配;
  5. JVM抛出 java.lang.OutOfMemoryError: Unable to create new native thread 错误。

示例

下面的代码在一个死循环中创建并启动很多新线程。代码执行后, 很快就会达到操作系统的限制, 报出 java.lang.OutOfMemoryError: Unable to create new native thread 错误。

原生线程的数量由具体环境决定, 比如, 在 Windows, Linux 和 Mac OS X 系统上:

  • 64-bit Mac OS X 10.9, Java 1.7.0_45 – JVM 在创建 #2031 号线程之后挂掉
  • 64-bit Ubuntu Linux, Java 1.7.0_45 – JVM 在创建 #31893 号线程之后挂掉
  • 64-bit Windows 7, Java 1.7.0_45 – 由于操作系统使用了不一样的线程模型, 这个错误信息似乎不会出现. 创建 #250,000 号线程之后,Java进程依然存在, 但虚拟内存(swap file) 的使用量达到了 10GB, 系统运行极其缓慢,基本上没法运行了。

所以如果想知道系统的极限在哪儿, 只需要一个小小的测试用例就够了, 找到触发 java.lang.OutOfMemoryError: Unable to create new native thread 时创建的线程数量即可。

解决方案

有时可以修改系统限制来避开 Unable to create new native thread 问题. 假如JVM受到用户空间(user space)文件数量的限制, 像下面这样,就应该想办法增大这个值:

[root@dev ~]# ulimit -a
core file size (blocks, -c) 0
...... 省略部分内容 ......
max user processes (-u) 1800

更多的情况, 触发创建 native 线程时的OutOfMemoryError, 表明编程存在BUG. 比如, 程序创建了成千上万的线程, 很可能就是某些地方出大问题了 —— 没有几个程序可以 Hold 住上万个线程的。

一种解决办法是执行线程转储(thread dump) 来分析具体情况。 先定位到问题的根源所在,在解决问题。

本文是全系列中第5 / 8篇:java内存溢出系列

本站大部分下载资源收集于网络,只做学习和交流使用,版权归原作者所有,若为付费资源,请在下载后24小时之内自觉删除,若作商业用途,请到原网站购买,由于未及时购买和付费发生的侵权行为,与本站无关。本站发布的内容若侵犯到您的权益,请联系本站删除,我们将及时处理!