问题描述
系统部署后遇到这个问题,因该环境提供的服务器配置偏低导出出现 java.lang.OutOfMemoryError: unable to create new native thread异常
问题分析
问题本质原因是项目创建了线程,而系统能创建的线程数是有限制的,导致了异常的发生。能创建的线程数的具体计算公式如下:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
当创建线程时虚拟机会在JVM内存创建线程对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。由公式得出结论:你给JVM内存越多,那么你能创建的线程越少,越容易发生 java.lang.OutOfMemoryError: unable to create new native thread
问题解决
修改linux 最大进程数
1.查看用户打开到最大进程数
ulimit -a
max user processes (-u) #系统限制某用户下最多可以运行多少进程过线程
2.修改进程数
1.在/etc/security/limits.conf 文件里添加如下内容
* soft nproc 65535 #打开进程数
* hard nproc 65535 #打开进程数
操作方法:
echo "* soft nproc 65535" >> /etc/security/limits.conf
echo "* hard nproc 65535" >> /etc/security/limits.conf
在 /etc/sysctl.conf 中添加 kernel.pid_max = 65535
# vim /etc/sysctl.conf
kernel.pid_max = 65535
或者
echo "kernel.pid_max = 65535" >> /etc/sysctl.conf
然后重启机器。
保存退出,输入以下命令执行使其生效
其他问题排查
1.如果程序中有bug,导致创建大量不需要的线程或者线程没有及时回收,那么必须解决这个bug,修改参数是不能解决问题的。
2.如果程序确实需要大量的线程,现有的设置不能达到要求,那么可以通过修改MaxProcessMemory,JVMMemory,ThreadStackSize这三个因素,来增加能创建的线程数:
MaxProcessMemory 使用64位操作系统
VMMemory 减少 JVMMemory 的分配
ThreadStackSize 减小单个线程的栈大小
3.优化代码里面的线程池的线程配置,减少 JVMMemory 的分配
思考问题
具体的排查方法
1:通过堆栈信息,确定是否存在创建线程过多的问题
创建了多少线程?过度创建的线程的堆栈信息是什么?谁创建了这些线程?
一旦了解清楚,就能很快解决OOM的问题
2:增加操作系统的线程数
通过ulimit -a查看操作系统的最大线程数,这里假设为1024
如果应用程序所需的线程数超过1024,可以通过增大操作系统的线程数解决问题
3:增加机器内存
如果发现操作系统的线程数并未达到上限,说明应用程序需要更多的内存来创建线程
此时,增加机器内存便可以解决问题
4:减少JVM堆内存(-Xmx)
增加机器内存涉及硬件操作,不是最佳的选择
这时,不要忘记:线程不是在JVM的堆内存中创建的!
如果为JVM分配堆内存后,剩余内存较少,也会导致OOM: unable to create new native thread
在JDK 1.8中,需要减少–Xmx配置的值;在JDK 1.7中,需要减少堆内存和永久代内存( –Xmx和-XX:MaxPermSize)
5: 减少进程数
减少进程数,与较少堆内存相似
如果Java进程数过多,即使每个进程使用的堆内存不大,但累积起来也会导致剩余内存较少,从而出现OOM: unable to create new native thread
6: 减少线程栈大小(-Xss)
如果每个线程的堆栈较大,应用程序整体消耗的内存也会变大
可以根据实际情况,减少线程的堆栈大小,即通过–Xss设置线程堆栈为一个合理的、较小值
注意:-Xss不是设置得越小越好,过小的-Xss值会导致应用程序出现java.lang.StackOverflowError错误,甚至都无法启动JVM