记一次tomcat线程创建异常调优:unable to create new native thread
测试在进行一次性能测试的时候发现并发300个请求时出现了下面的异常:
HTTP Status 500 - Handler processing failed; nested exception is java.lang.OutOfMemoryError: unable to create new native thread
看到这个异常有点发慌,毕竟并发程序写的少,突然来这么一个确实有点找不着背。但不管怎么样还是先搜索一下是啥原因吧。
这个错误是因为无法再创建新线程导致的,原因可能是没有更多的空间用于创建线程,还有一个公式用来计算:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
这其中各个参数的意思是:
MaxProcessMemory:进程最大寻址空间。
JVMMMEMORY:jvm的内存空间(堆+永久区)-Xmx大小 (应该是实际分配大小)
ReservedOsMemory:操作系统预留内存
ThreadStackSize:-Xss大小
于是我查看一下liunx系统的参数情况:
[root@RHEL63temp ~]# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 63628
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
其中的max user processes只有1024个,心想着这事情可能问题不在于创建的限制,而是为什么要创建这么多线程?毕竟只有300个并发,tomcat最多也就300个线程用于处理请求吧?
于是想了想程序代码的问题,还是要从代码上去查找原因。于是临时开始研究了一下JVisualVM这个监控工具,在服务器上做了做配置,反正网上有教程。因为我使用的是Tomcat,所以直接就监控Tomcat吧,在catalina.sh中增加一些参数:
JAVA_OPTS="-server -Xmx384m -Xms128m -XX:PermSize=128M -XX:MaxPermSize=256m"
-Dcom.sun.management.jmxremote.port=9998
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.rmi.server.hostname=192.168.49.199"
这样就可以使用JVisualVM通过JMX方式监控了。连接上后再进行测试问题原因找到了。
线程产生这么大主要是两块:
1、tomcat的本身需要支持并发的线程
2、smack产生的大量线程,而且连续压测会发现smack的线程出现不释放的情况
这里的关键是smack的使用,因为系统实现了一个功能就是通过网页发起ajax请求,然后在服务端模拟即时通过用户发送消息。因为并发300个请求,导致每个请求都要创建smack的连接,而smack是用于客户端开发的库,启动后会创建3个左右的线程用于连接和处理服务器的通讯。这就导致同时会产生300*3的线程,所以并发时会所线程创建数用满。
既然问题原因找到了,所以这里的问题可能还是smack的使用问题,毕竟smack是个客户端库,不太适合于这种服务端的场景。
解决方法是使用其他方式代替smack发消息,这样只需要创建少量的线程就可以满足要求,而且处理速度大大提升。