linux tomcat java.lang.OutOfMemoryError: unable to create new native thread
通过该异常的名字就可以知道:
该问题是内存溢出,不能创建新的线程。
该问题的引发是由于我们系统的用户量变大了而引起的。
附带一篇测试最大线程数的脚本
-
import java.util.concurrent.CountDownLatch;
-
-
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);
-
-
public HoldThread() {
-
this.setDaemon( true);
-
}
-
-
public void run() {
-
try {
-
cdl.await();
-
} catch (InterruptedException e) {
-
}
-
}
-
}
解决该问题的方案有几种:
一、根据linux下的用户创建的最大线程数进行调整(该做法需抛出root用户[因为root用户已经拥有最大的线程数])
具体做法:【使用ulimit -u 命令查看用户用于的线程数】
[root@weixinkaifaapp-36 ~]# ulimit -u
31486
[root@weixinkaifaapp-36 ~]# su - test
Last login: Tue Aug 15 10:03:33 CST 2017 on pts/1
[test@weixinkaifaapp-36 ~]$ ulimit -u
4096
[test@weixinkaifaapp-36 ~]$
以上可以看出我的root用户拥有可31486个线程的权限[我们生产实际的配置有2万5千个]
但是test用户只能创建4096个线程,一般应用的启动都是由较低权限的用户启动的,故在系统中的创建的线程数超过了4096些时,就会导致系统的内存溢出。
解决方案:
修改配置文件
$ cd /etc/security/limits.d/
$ ll
【查看配置列表 不同的系统版本所查到的配置名称不一定相同】
$ vim 20-nproc.conf
# Default limit for number of user's processes to prevent
# accidental fork bombs.
# See rhbz #432903 for reasoning.
* soft nproc 4096
root soft nproc unlimited
可以看到在root用户下拥有最大的线程数,而其他用户只有4096个线程数创建的权利。
具体应该配置多大的线程数需要根据不同的需求而定了。
二、使用Apache Tomcat 集群
这种做法可以使用nginx进行分发,然后使用redis进行控制session回话,使用nfs进行资源共享,做法比较简单,但是缺点是公司投入较大,由于我们公司并不额外提供服务器,故这个做法,我这里没有进行实现。(但是我们的系统是nginx+2个tomcat+2个服务器+1个nfs服务器组成的)
三、需要tomcat启动的jvm参数(这里不建议这样配置,因为风险高,需要测试很久)
具体计算公式如下:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory 指的是一个进程的最大内存
JVMMemory JVM内存
ReservedOsMemory 保留的操作系统内存
ThreadStackSize 线程栈的大小
在java语言里, 当你创建一个线程的时候,虚拟机会在JVM内存创建一个Thread对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。
结合上面例子我们来对公式说明一下:
MaxProcessMemory 在64位的 windows下是 4G
JVMMemory eclipse默认启动的程序内存是64M
ReservedOsMemory 一般是130M左右
ThreadStackSize 32位 JDK 1.6默认的stacksize 325K左右
公式如下:
(4*1024*1024-64*1024-130*1024)/325 = 12294
由公式得出结论:你给JVM内存越多,那么你能创建的线程越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread。
因为ThreadStackSize 越大 除的越多,创建的线程也就越少。
对于linux系统内存的查看可以使用free命令[默认单位是k]
但是我一般使用free -m命令通过以M的方式显示系统内存
[root@weixinkaifaapp-36 ~]# free -m
total used free shared buffers cached
Mem: 3953 3818 135 51 0 43
-/+ buffers/cache: 3774 179
Swap: 3983 3983 0
[root@weixinkaifaapp-36 ~]#
total为该系统总共的内存大小,used为已使用的内存大小,free为空闲的内存大大小。
对于tomcat进行配置jvm:
linux下修改Catalina.sh文件[在{CATALINA_HOME}/bin目录下]
添加:JAVA_OPTS='-Xms512m -Xmx1024m'
要加“m”说明是MB,否则就是KB了,在启动tomcat时会报内存不足。
-Xms:初始值
-Xmx:最大值
-Xmn:最小值
例如我们配置的:
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true "
然后重启tomcat,配置即可生效。
最后列几个重要的参数:
(1)-Xms,jvm启动时,初始分配的堆/栈内存
(2)-Xmx,JVM最大允许分配的堆/栈内存,按需分配
(3)-Xss,设定每个线程的堆栈大小
(4)-XX:PermSize,JVM初始分配的非堆内存
(5)-XX:MaxPermSize,JVM最大允许分配的非堆内存,按需分配