OutOfMemoryError系列(5): Unable to create new native thread

这是本系列的第五篇文章, 相关文章列表:

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

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

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

java-lang-outofmemoryerror-unable-to-create-new-native-thread

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 错误。

 while(true){
    new Thread(new Runnable(){
        public void run() {
            try {
                Thread.sleep(10000000);
            } catch(InterruptedException e) { }        
        }    
    }).start();
} 

原生线程的数量由具体环境决定, 比如, 在 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) 来分析具体情况。 一般需要花费好几个工作日来处理。 当然, 我们推荐使用 Plumbr 来找出问题的根源, 分分钟帮你搞定。

原文链接: https://plumbr.eu/outofmemoryerror/unable-to-create-new-native-thread

翻译日期: 2017年9月21日

翻译人员: 铁锚: http://blog.youkuaiyun.com/renfufei

<think>好的,用户遇到了Java程序中的OutOfMemoryError,具体是无法创建新的本地线程的问题。我需要根据之前的引用和已有的知识来思考解决方案。 首先,这个错误通常是因为Java应用达到了线程数的系统限制。需要从多个方面分析原因。可能的原因包括系统级别的线程数限制、JVM设置、线程栈大小配置不当,或者程序本身创建了太多线程没有及时释放。 根据引用[1],错误信息明确指出线程创建达到上限。引用[2]提到几个关键因素:MaxProcessMemory(进程最大内存)、JVMMemory(JVM内存)、ReservedOsMemory(保留的操作系统内存)、ThreadStackSize(线程栈大小)。这些都是需要考虑的参数。 接下来,我需要整理解决方案的步骤。首先,检查系统级别的限制,比如ulimit -u的结果,以及操作系统的全局限制(如Linux的pid_max和threads-max)。然后调整这些限制,但要注意系统资源是否足够。 第二,优化JVM参数,特别是减少线程栈大小(-Xss),这样可以在相同内存下创建更多线程。但要注意栈溢出风险。 第三,检查程序中的线程使用情况,是否有线程泄漏,或者可以改用线程池来复用线程,避免频繁创建和销毁。 另外,引用[2]提到MaxProcessMemory的计算,可能需要调整进程的虚拟内存设置,但具体方法因系统而异。对于32位系统,可能受限于地址空间,升级到64位可能有效。 还要考虑减少内存占用,释放资源,或者水平扩展应用,分散负载到多个实例。 最后,用户可能需要具体的操作步骤,比如如何修改系统参数、调整JVM选项,以及代码优化的示例。需要确保建议安全可行,并提醒用户注意调整参数时的潜在影响,比如降低栈大小可能导致栈溢出,增加系统限制可能消耗更多资源。 总结解决方案的步骤,分点列出,确保清晰易懂。同时生成相关问题,帮助用户进一步理解和排查问题。</think>### 解决方案:Java OutOfMemoryError: Unable to create new native thread 该错误表明Java应用触发了系统级线程创建限制[^1],需从以下方向逐步排查和优化: --- #### 1. **检查系统级线程限制** - **Linux系统** 执行 `ulimit -u` 查看当前用户允许的最大线程数。 修改 `/etc/security/limits.conf` 文件,例如: ```bash * soft nproc 4096 * hard nproc 65536 ``` 需重启生效。 - **全局系统限制** Linux系统中检查 `/proc/sys/kernel/pid_max`(最大进程数)和 `/proc/sys/kernel/threads-max`(全局线程数上限),通过 `sysctl` 调整。 --- #### 2. **优化JVM参数配置** - **减小线程栈大小** 通过 `-Xss` 参数降低单个线程栈的内存占用(默认1MB),例如: ```bash -Xss256k ``` 注意:过小可能导致 `StackOverflowError`。 - **控制JVM堆内存** 避免堆内存占用过多导致操作系统预留内存不足,例如: ```bash -Xms512m -Xmx2g ``` --- #### 3. **优化程序线程管理** - **使用线程池** 避免无限制创建线程,改用 `ThreadPoolExecutor` 控制并发量: ```java ExecutorService executor = Executors.newFixedThreadPool(100); // 根据系统能力设置 ``` - **排查线程泄漏** 通过 `jstack <pid>` 或可视化工具(如VisualVM)分析线程状态,确保线程任务结束后能被回收。 --- #### 4. **调整操作系统参数(进阶)** - **虚拟内存分配** 在Linux中增大进程虚拟内存限制(需root权限): ```bash sysctl -w vm.max_map_count=262144 ``` - **升级64位系统** 32位系统受限于进程地址空间(通常2-4GB),迁移到64位系统可显著提升资源上限[^2]。 --- #### 5. **架构级优化** - **水平扩展** 通过分布式部署将负载分散到多个实例。 - **异步非阻塞模型** 使用Netty或Vert.x等框架减少线程依赖。 --- ### 关键公式说明 系统允许的最大线程数可通过以下公式估算[^2]: $$ \text{MaxThreads} = \frac{\text{MaxProcessMemory - JVMMemory - ReservedOsMemory}}{\text{ThreadStackSize}} $$ 例如:若系统剩余内存为2GB,单个线程栈256KB,则理论最大线程数约 $2 \times 1024^2 / 256 = 8192$。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值