线程溢出
# 线程溢出
# 报错信息
java.lang.OutOfMemoryError : unable to create new native Thread
- 出现这种异常,基本上都是创建了大量的线程导致的
# 案例模拟
说明:操作系统会崩溃,linux无法再进行任何命令,mac/windows可能直接关机重启。鉴于以上原因,建议在虚拟机进行测试。
示例代码:
public class TestNativeOutOfMemoryError {
public static void main(String[] args) {
for (int i = 0; ; i++) {
System.out.println("i = " + i);
new Thread(new HoldThread()).start();
}
}
}
class HoldThread extends Thread {
CountDownLatch cdl = new CountDownLatch(1);
@Override
public void run() {
try {
cdl.await();
} catch (InterruptedException e) {
}
}
}
运行结果:
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)
# 分析及解决
方向一:JVM设置
通过
-Xss
设置每个线程栈大小的容量JDK 5.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
的大小来控制线程数量。
方向二:线程总数也受到系统空闲内存和操作系统的限制,检查是否该系统下有此限制:
/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区域仅占用少量的内存,那么调低这个值可以帮助释放内存给内核用。
上次更新: 5/28/2023, 10:57:53 PM