问题
一个java服务运行差不多一天就会崩溃,报java.lang.OutOfMemoryError: unable to create new native thread
分析
可能原因1:服务器设置无法满足服务创建的线程数量
可能原因2:jvm配置无法满足创建的线程数量
可能原因3:代码问题,有死循环,线程在无限创建
解决
1.查看服务器linux用户的最大进程数,挺大的跟这个应该没关系
[root@localhost]# ulimit -u
126985
2.调节jvm启动时设置的每个线程的堆栈大小参数 Xss,原来是默认值1m,可以改成Xss256k,先看下一步;
3.其实分析下来大概率代码是有问题了,应该直接从这一步切入的,前面两步算是一种拓展学习了
先用“ps -ef|grep java”查看服务的PID(本文出问题的服务PID是134859,后文也有用到)
然后查看服务的进程数命令“ps hH p PID | wc -l”,显示304个进程,看似很正常
[root@localhost]# ps hH p 134859 | wc -l
304
过一会再看,没多长时间就多了两百多
[root@localhost]# ps hH p 134859 | wc -l
568
很明显这不正常,有什么代码在循环创建线程
jstack命令可以用来查看Java线程的调用堆栈的,可以用来分析线程问题,打印服务的线程日志到dump.txt文件
[root@localhost]# jstack -l 134859 >> dump.txt
补充说明:可以使用top -Hp PID 命令查看PID进程中的线程占用信息,然后根据查看到的占用高的线程PID换算成16进制,再从jstack导出的线程堆栈信息中查找这个16进制PID的报错信息
然后查看这个文件,发现大量类似于下面的线程TIMED_WAITING的记录
"threadPoolTaskExecutor-11" #54 prio=5 os_prio=0 tid=0x00007fc760036800 nid=0xbec3 waiting on condition [0x00007fc81fffe000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at cn.doeat.blog.test.thr.PostStatusThr.run(PostStatusThr.java:97)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- <0x00000005d20dc8e0> (a java.util.concurrent.ThreadPoolExecutor$Worker)
"threadPoolTaskExecutor-10" #53 prio=5 os_prio=0 tid=0x00007fc760034800 nid=0xbec2 waiting on condition [0x00007fc824191000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at cn.doeat.blog.test.thr.PostStatusThr.run(PostStatusThr.java:97)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- <0x00000005d20dbf68> (a java.util.concurrent.ThreadPoolExecutor$Worker)
······
······
······
日志中有明确的出问题代码的位置,PostStatusThr.java文件第97行
at cn.doeat.blog.test.thr.PostStatusThr.run(PostStatusThr.java:97)
去看代码,是个线程的run方法,97行只是个break,看代码逻辑没有什么错误,就不贴出代码了
public class PostStatusThr implements Runnable {
private boolean endFlag = true;
private long sleepTime;
private int maxMessageCount = 10;
public PostStatusThr(boolean endFlag, long sleepTime,int maxMessageCount) {
this.endFlag = endFlag;
this.sleepTime = sleepTime;
this.maxMessageCount = maxMessageCount;
}
@Override
public void run() {
···
···
···
}
}
PostStatusThr中有个有参的构造方法,那就看谁调用了这个类,发现只有PostStatusTask类调用了PostStatusThr的有参构造方法,此处也没有什么问题,只是运行创建线程池
@Component
public class PostStatusTask {
@Autowired
private ThreadPoolTaskExecutor executor;
public void run() {
try {
int i = 0;
while (i < 2) {
executor.execute(new PostSensorStatusThr(true, 5000,10));
i++;
}
} catch (Exception exc) {
exc.printStackTrace();
}
}
}
那再看看哪里调用了PostStatusTask类,寻根溯源找到了一处代码,这里相当于每隔3秒初始化一次线程池····,代码原本意图是项目启动时初始化调用创建线程池
@Component
public class InitializationTask {
@Scheduled(fixedDelay =3000)
public static synchronized void pollingLogs() {
ServiceUtil.getPostStatusTask().run();
}
}
修改代码使符合预期效果,springboot项目实现ApplicationRunner接口会在项目启动时运行实现类中的run方法
@Component
public class InitializationTask implements ApplicationRunner {
public static synchronized void pollingLogs() {
ServiceUtil.getPostStatusTask().run();
}
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
pollingLogs();
}
}