先来了解下Linux系统的最大线程数限制的参数有哪些:
- /proc/sys/kernel/threads-max:内核所能使用的线程的最大数目,由物理内存决定,32G内存的默认值为255928。
- /proc/sys/vm/max_map_count:限制一个进程可以拥有的VMA(虚拟内存区域)的数量,默认值为65530。
- ulimit -u:单个用户允许的最大进程或线程上限,默认root用户不限制,其他用户为4096。
- /proc/sys/kernel/pid_max:操作系统线程数限制。为了兼容旧版(旧版本32位操作系统的最大值为32768),默认为32768。PS:其实64位操作系统最多可以设置为2^22,400多万。
问题出现前这些参数的实际值
在问题出现之前,ulimit -u的已经调整到了404800,其他的都为默认值。
问题描述
xshell连接服务器时报错:fork:cannot allocate memory
。
解决过程
其实这个报错直接就可以定位到是pid_max的问题。
查询当前整个系统已用的线程或进程数:
$ pstree -p | wc -l
32602
###这是问题解决之后查的,问题产生时的值应该比32768略小些。
问题已经很明确了,就是pid_max的值不够大造成的。
知道了问题,那就好办了,直接修改/proc/sys/kernel/pid_max
的值:
#临时生效
$ echo 1000000 > /proc/sys/kernel/pid_max
#永久生效
$ echo "kernel.pid_max=1000000 " >> /etc/sysctl.conf
$ sysctl -p
修改之后,xshll可以登录了。
其实这中间还是蛮曲折的:在执行命令的时候,一直报fork:cannot allocate memory
,没办法,只能一直试,后来总算能勉强执行命令了。但是当时是用非root用户登录的,命令需要用root用户执行,在su - root
时,也是一直报这个错。然后尝试了阿里云UI中的vnc远程登录也是如此。没办法,只能将一个相对不那么重要的服务先kill掉,然后再切到root用户执行上面的命令。
思考
虽然这台主机中跑了比较多(二十几个)的java模块,但是3万多的线程数,平均一个模块要产生1000+的线程?我的预感是这其中应该有某个(些)模块产生了异常多的线程。
写个脚本验证下:
#!/bin/bash
for pid in `jps | awk '{print $1}'`
do
num=`pstree -p $pid | wc -l`
echo $pid
echo $num
echo " "
done
执行结果:
$ ./check_thread.sh
21056
105
15170
162
20355
70
20422
65
9611
144
31180
203
6029
64
22606
183
21011
260
14932
148
21464
175
14491
87
22747
64
1819
99
10971
155
46556
119
9886
47
7007
146
20707
166
21285
81
6572
150
22318
75
9970
59
10099
36
51827
0
9973
54
6646
73
5047
29228
20026
181
33087
63
可以看到pid为5047的进程产生了29228个线程,然后根据pid就可以直接找到这个模块了。
解决
第二天上班之后,跟负责这个模块的开发人员反映了这个问题。在一番仔细的查看代码之后,终于是找到了问题的根本所在—连接solr的http连接一直没有关闭导致的。
知道问题所在之后,解决起来就很快了。
至此,一个线上的线程数暴增的问题顺利得到了解决。