性能文章>OutOfMemoryError之unable to create new native thread原因分析及6种解决方案>

OutOfMemoryError之unable to create new native thread原因分析及6种解决方案原创

770927

java.lang.OutOfMemoryError:unable to create new native thread 是比较常见的一种异常,表示应用程序无法创建新的线程。

产生该异常,总体上可总结为两个原因:

  1. 服务器剩余内存不足;
  2. 线程数超过系统限制。

问题定位与解决方案

通过分析异常可能产生的原因,可以通过选择以下合适的方案进行排查解决。

1. 是否创建了过多的线程

如果有对进程的线程数进行监控,可以查看线程创建的趋势,以及当前已创建的线程数,确认线程数是否合理。

同时,可以通过 jstack -l 将线程栈dump下来。线程栈的作用,一方面如果没监控可以用来检查已创建的线程数,另一方面可以可以通过分析栈的调用关系,确定是哪里在创建线程。

这里,有两个比较好的分析工具,一个是广为人知的 http://fastthread.io/ ,另一个是新秀 https://www.perfma.com/product/thread

通过这些工具,可以比较直观的看到线程的整体创建情况,例如普通线程、守护线程的数量与占比,各个状态的线程数量等。

2. 操作系统对线程数的限制

操作系统对线程数的创建是有限制的,这个限制可以通过 ulimit -u 来查看。

如果应用程序创建的线程数,超过了系统限制的最大线程数,也会抛出 unable to create new native thread 异常。

如果遇到了这种情况,通过适当的调大该值,可以解决问题。

如果要临时修改这个限制,可以通过执行 ulimit -u 65536 进行调整。

更好的方式,是将 ulimit -u 65536 写到启动脚本中,如 ~/.bashrc 或其他启动脚本。这样就不用每次都去手工修改了。

3. 服务器内存不足

如果用户创建的线程数在合理的范围内,也没有超过 ulimit -u 设置的值。

那么,为了确保能够正确的创建新的线程,可以考虑增加服务器的物理内存。

4. 进程的堆大小

需要注意的是,线程所使用的内存,并不在Java堆中分配。

因此,即使Java堆空间是充足的,如果剩余的物理内存太小,无法满足更多线程创建所需的内存时,也会抛出该异常。

一个例子:

假设服务器的物理内存为 6G。

Java堆大小为 5G Heap + 512M Perm Gen,共占用 5.5G。

那么,剩余的物理内存仅为 6-5.5=0.5G。

而这 0.5G 的内存空间,将用于运行内核进程,以及其他的用户进程。Java虚拟机进程本身其实也会占用一部分的内存。

显然,除去这些进程所占用的内存,剩下的空间已经很有限。

在无法满足更多线程创建所需的内存时,将会抛出该异常。

在不影响Java进程的情况下,可以考虑减少Java堆内存的分配,从而腾出更多的空间用于创建线程。

5. 服务器的进程数

我们通常希望能够最大化的利用服务器的资源,提高资源的利用率,避免浪费资源。

于是,在一台服务器上部署了多个进程。

可能由于进程数创建过多,或者进程内存不够扩大之后,导致物理机所剩的内存不多。

从结果上看,与进程的堆大小占用内存过多是类似的。

解决方法比较简单,就是减少服务器运行的进程数,将进程迁移到其他服务器。

6. 线程栈的大小

既然每一个线程都需要占用一定的内存,那有没有办法给线程分配小一点内存呢?

对于不同的Java虚拟机版本,有一个默认的线程栈大小。

早期默认大小为256K,现在一般为1M。

通过JVM的 -Xss 参数,可以调整该内存大小。

假设我们要创建500个线程。

如果每个线程需要1M内存,那么线程占用的总内存为500M。

如果线程栈设置为512K,那么线程占用的内存将减少一半,为250M。

需要注意的是,如果线程栈设置得过小,当线程请求分配的栈容量不足时,将会抛出StackOverflowError异常。

小结

总体上,当遇到java.lang.OutOfMemoryError:unable to create new native thread异常时,可以通过监控在OOM之前,服务器剩余内存、进程创建的线程数以及 ulimit -u 的结果进行综合排查解决。

同时,可以通过 jstack -l 将线程栈dump下来。进一步分析具体是哪里创建了线程,是否合理。

原因分析及6种解决方案:

  1. 创建了过多的线程:减少线程数;
  2. 操作系统对线程数的限制:调大线程数限制;
  3. 服务器内存不足:增加物理内存;
  4. 进程的堆内存太大:减小堆内存大小;
  5. 服务器的进程数太多:减少进程数;
  6. 线程栈的太大:调小线程栈。

本文翻译总结自:
https://dzone.com/articles/troubleshoot-outofmemoryerror-unable-to-create-new

请先登录,再评论

有一个问题就是,设置了XSS参数之后,发现创建线程的数量还是一致的,就是说其实线程栈大小去申请的时候,操作系统其实是不会给你所设置的空间大小,比如设置-Xss10M 但是实际上操作系统可能给我256K就够用了,所以不管是设置Xss25K 还是设置Xss10M 最后创建的线程数量都是一样的

1年前

归类还满齐全的,翻译不易👍

2年前

为你推荐

字符串字面量长度是有限制的
前言 偶然在一次单元测试中写了一个非常长的字符串字面量。 正文 在一次单元测试中,我写了一个很长的字符串字面量,大概10万个字符左右,编译时,编译器给出了异常告警 `java: constant
多次字符串相加一定要用StringBuilder而不用-吗?
今天在写一个读取Java class File并进行分析的Demo时,偶然发现了下面这个场景(基于oracle jdk 1.8.0_144): ``` package test; public c
如何通过反射获得方法的真实参数名(以及扩展研究)
前段时间,在做一个小的工程时,遇到了需要通过反射获得方法真实参数名的场景,在这里我遇到了一些小小的问题,后来在部门老大的指导下,我解决了这个问题。通过解决这个问题,附带着我了解到了很多新的知识,我觉得
高吞吐、低延迟 Java 应用的 GC 优化实践
本篇原文作者是 LinkedIn 的 Swapnil Ghike,这篇文章讲述了 LinkedIn 的 Feed 产品的 GC 优化过程,虽然文章写作于 April 8, 2014,但其中的很多内容和
「每日五分钟,玩转 JVM」:久识你名,初居我心
聊聊 JVMJVM,一个熟悉又陌生的名词,从认识Java的第一天起,我们就会听到这个名字,在参加工作的前一两年,面试的时候还会经常被问到JDK,JRE,JVM这三者的区别。JVM可以说和我们是老朋友了
据说99.99%的人都会答错的类加载的问题
概述首先还是把问题抛给大家,这个问题也是我厂同学在做一个性能分析产品的时候碰到的一个问题。 同一个类加载器对象是否可以加载同一个类文件多次并且得到多个Class对象而都可以被java层使用吗请仔细注意
Java多线程——并发测试
编写并发程序时候,可以采取和串行程序相同的编程方式。唯一的难点在于,并发程序存在不确定性,这种不确定性会令程序出错的地方远比串行程序多,出现的方式也没有固定规则。那么如何在测试中,尽可能的暴露出这些问
Java多线程知识小抄集(一)
本文主要整理笔者遇到的Java多线程的相关知识点,适合速记,故命名为“小抄集”。本文没有特别重点,每一项针对一个多线程知识做一个概要性总结,也有一些会带一点例子,习题方便理解和记忆。 1.interr