一、报错信息

java.lang.OutOfMemoryError : unable to create new native Thread

二、问题原因

出现这种异常,基本上都是创建了大量的线程导致的

三、案例模拟

说明

操作系统会崩溃,linux无法再进行任何命令,mac/windows可能直接关机重启。鉴于以上原因,我们在虚拟机进行测试。

示例代码

  1. package com.atguigu.oom;
  2. import java.util.concurrent.CountDownLatch;
  3. /**
  4. * 测试4:线程溢出
  5. * @create 17:45
  6. */
  7. public class TestNativeOutOfMemoryError {
  8. public static void main(String[] args) {
  9. for (int i = 0; ; i++) {
  10. System.out.println("i = " + i);
  11. new Thread(new HoldThread()).start();
  12. }
  13. }
  14. }
  15. class HoldThread extends Thread {
  16. CountDownLatch cdl = new CountDownLatch(1);
  17. @Override
  18. public void run() {
  19. try {
  20. cdl.await();
  21. } catch (InterruptedException e) {
  22. }
  23. }
  24. }

运行结果

i = 15241
Exception in thread “main” java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:717)
at TestNativeOutOfMemoryError.main(TestNativeOutOfMemoryError.java:9)
image.png

四、分析及解决

解决方向1

通过 -Xss 设置每个线程栈大小的容量

JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。

正常情况下,在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

能创建的线程数的具体计算公式如下:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
——————————————————————————————————————
MaxProcessMemory 指的是进程可寻址的最大空间
JVMMemory JVM内存
ReservedOsMemory 保留的操作系统内存
ThreadStackSize 线程栈的大小
——————————————————————————————————————
在Java语言里, 当你创建一个线程的时候,虚拟机会在JVM内存创建一个Thread对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。

由公式得出结论:你给JVM内存越多,那么你能创建的线程越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread


综上,在生产环境下如果需要更多的线程数量,建议使用64位操作系统,如果必须使用32位操作系统,可以通过调整Xss的大小来控制线程数量。

解决方向2

线程总数也受到系统空闲内存和操作系统的限制,检查是否该系统下有此限制:

/proc/sys/kernel/pid_max 系统最大pid值,在大型系统里可适当调大

/proc/sys/kernel/threads-max 系统允许的最大线程数

maxuserprocess(ulimit -u) 系统限制某用户下最多可以运行多少进程或线程

/proc/sys/vm/max_map_count
max_map_count文件包含限制一个进程可以拥有的VMA(虚拟内存区域)的数量。虚拟内存区域是一个连续的虚拟地址空间区域。在进程的生命周期中,每当程序尝试在内存中映射文件,链接到共享内存段,或者分配堆空间的时候,这些区域将被创建。调优这个值将限制进程可拥有VMA的数量。限制一个进程拥有VMA的总数可能导致应用程序出错,因为当进程达到了VMA上线但又只能释放少量的内存给其他的内核进程使用时,操作系统会抛出内存不足的错误。如果你的操作系统在NORMAL区域仅占用少量的内存,那么调低这个值可以帮助释放内存给内核用。
image.png