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

1. 理论准备

线程本质上是进程,显著区别在于线程运行在共享内存空间,而进程运行在独立内存空间,以下是相关参数:

  • /proc/sys/kernel/pid_max 决定的进程数是运行于独立内存空间的进程

  • /proc/sys/kernel/threads-max 决定的线程数是运行于共享内存空间的线程

  • ulimit -u 指的是一个用户在同一时间最多能拥有的总进程(含线程)

  • ulimit 的进程数(含线程)受限于 pid_max 及 threads-max

2. 排查

一般由于两个原因导致的:

1)内存空间不足以满足创建线程所需的stack size virtual memory < stack size*the number of threads 2)线程数已达到操作系统的上限

最大线程数计算方法:

# 计算公式为:
default_nproc = max_threads / 2;
# 其中, max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);
# mempages是机器的物理页面个数, THREAD_SIZE=8K, 所以, 计算公式为:
default_nproc = max_threads / 2
              = (mempages * PAGE_SIZE) / ( 2 * 8 *THREAD_SIZE )
              = total_memory / 128K;

# 计算本机默认nproc配置:
cat /proc/meminfo | grep MemTotal
MemTotal:       115571480 kB

echo "115571480 / 128" | bc
902902

ulimit -u
902682
# 算出来default_nproc = 902902, 和实际的902682很接近,
# 因为物理页面会存储一些关键数据, 所以实际的比计算出来的要小一些.

2.1. 线程数

jvm stack size 相关参数
java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
操作系统相关参数
#进程可用最大虚拟内存
ulimit -v

#最大栈大小
ulimit -s

#每个用户可创建最大进程数
ulimit -u

#查看全部设置
ulimit -a

#系统内存
free -h

#java线程设置
java -XX:+PrintFlagsFinal -version | grep ThreadStackSize

#系统支持的最大进程数
cat /proc/sys/kernel/pid_max

#共享内存最大线程数
cat /proc/sys/kernel/threads-max

#查询当前系统已用的进程数
ps -elf | wc -l

#查询当前系统已用的线程和进程数
ps -eLf | wc -l

#获取进程id
ps -ef | grep <key word>

#查询当前某程序的线程或进程数
ps -p <pid> -Lf | wc -l

2.2. 监控

内存使用率
free -m | awk 'NR==2{printf "%s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'
可用内存
freeMem=`free -m | awk 'NR==2{printf "%sMB\n", $4}'`
threadNum=`ps -eLf | wc -l`
dateS=`date "+%Y%m%d"`
timeS=`date "+%H:%M:%S"`
echo "$timeS $threadNum $freeMem" >> /tmp/$dateS.indicator