java.lang.OutOfMemoryError:unable to create new native thread 是比较常见的一种异常,表示应用程序无法创建新的线程。
产生该异常,总体上可总结为两个原因:
通过分析异常可能产生的原因,可以通过选择以下合适的方案进行排查解决。
如果有对进程的线程数进行监控,可以查看线程创建的趋势,以及当前已创建的线程数,确认线程数是否合理。
同时,可以通过 jstack -l 将线程栈dump下来。线程栈的作用,一方面如果没监控可以用来检查已创建的线程数,另一方面可以可以通过分析栈的调用关系,确定是哪里在创建线程。
这里,有两个比较好的分析工具,一个是广为人知的 http://fastthread.io/ ,另一个是新秀 https://www.perfma.com/product/thread
通过这些工具,可以比较直观的看到线程的整体创建情况,例如普通线程、守护线程的数量与占比,各个状态的线程数量等。
操作系统对线程数的创建是有限制的,这个限制可以通过 ulimit -u 来查看。
如果应用程序创建的线程数,超过了系统限制的最大线程数,也会抛出 unable to create new native thread 异常。
如果遇到了这种情况,通过适当的调大该值,可以解决问题。
如果要临时修改这个限制,可以通过执行 ulimit -u 65536 进行调整。
更好的方式,是将 ulimit -u 65536 写到启动脚本中,如 ~/.bashrc 或其他启动脚本。这样就不用每次都去手工修改了。
如果用户创建的线程数在合理的范围内,也没有超过 ulimit -u 设置的值。
那么,为了确保能够正确的创建新的线程,可以考虑增加服务器的物理内存。
需要注意的是,线程所使用的内存,并不在Java堆中分配。
因此,即使Java堆空间是充足的,如果剩余的物理内存太小,无法满足更多线程创建所需的内存时,也会抛出该异常。
一个例子:
假设服务器的物理内存为 6G。
Java堆大小为 5G Heap + 512M Perm Gen,共占用 5.5G。
那么,剩余的物理内存仅为 6-5.5=0.5G。
而这 0.5G 的内存空间,将用于运行内核进程,以及其他的用户进程。Java虚拟机进程本身其实也会占用一部分的内存。
显然,除去这些进程所占用的内存,剩下的空间已经很有限。
在无法满足更多线程创建所需的内存时,将会抛出该异常。
在不影响Java进程的情况下,可以考虑减少Java堆内存的分配,从而腾出更多的空间用于创建线程。
我们通常希望能够最大化的利用服务器的资源,提高资源的利用率,避免浪费资源。
于是,在一台服务器上部署了多个进程。
可能由于进程数创建过多,或者进程内存不够扩大之后,导致物理机所剩的内存不多。
从结果上看,与进程的堆大小占用内存过多是类似的。
解决方法比较简单,就是减少服务器运行的进程数,将进程迁移到其他服务器。
既然每一个线程都需要占用一定的内存,那有没有办法给线程分配小一点内存呢?
对于不同的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种解决方案:
本文翻译总结自:
https://dzone.com/articles/troubleshoot-outofmemoryerror-unable-to-create-new