线程“无故”死亡,究竟谁是幕后黑手?

来源:互联网 发布:PHP删除数组中指定元素 编辑:程序博客网 时间:2024/04/28 15:46

今天,一上班,同事就火急火燎的跑过来问我:程序怎么不会跑数据了,是不是挂了?按照惯例得先问问同事是不是哪里没配置好,导致没数据产生,在确认不是数据问题的之后,就纳闷了:难道程序真的挂了。

那个程序是我开发的一个多线程程序,run方法是一个死循环,负责处理数据,合适的情况下线程会进行休眠,避免浪费cpu资源。我第一时间登上服务器,使用jps命令可以看到程序的进程好好的还在那。由于之前数据库连接池出现过问题,所以我首先怀疑是不是因为连接池出问题导致访问不了数据库。然而通过观察日志,并没发现连接池不够用。紧接着,我便怀疑起是不是线程产生死锁,导致不会跑数据。于是便使用

jstack pid
命令查看堆栈,仔细的看了下里面的线程,发现并没有死锁,但是此时,通过观察堆栈,还是发现了些端倪:一开始我总共启动了20个线程,怎么现在只有那么寥寥几个。负责处理数据的那些线程全部壮烈牺牲。

是谁动了我的线程?

按理说运行这的线程如果出现问题,那么在程序日志里面肯定是能看到错误的,然而并没有!那究竟是谁动了我的线程。

第一个怀疑对象就是系统,但是仔细想想也不对,linux下面内存不够了,它直接开刀的是进程,但是我的进程依旧活的好好的,里面还有几个残留的线程在苟延残喘着,但是为了保险起见,我还是去看了下系统日志,发现并没有内存不足。

接下来,开始怀疑会不会是jvm搞的鬼。如果是程序使用内存高于启动参数设置的最大堆内存,会产生oom,jvm为了保证进程能活下来,会先把占内存的线程杀了。事情似乎渐渐明朗,由于启动程序的时候并没有设置 XX:+HeapDumpOnOutOfMemoryError ,所以即使发生oom,也会不产生dump文件。为了验证猜想,加入启动参数之后重新运行,果不其然,一段时间之后确实产生oom,使用jstack查看,堆栈里线程又剩下那么寥寥几个。此时大概可以断定,线程无缘无故死亡是因为oom导致线程被jvm杀了。通过用工具分析dump文件,发现无明显程序原因导致oom,所以应该是启动时分配的内存有点小。调大堆内存之后重新运行,不会再产生oom。


写了个模拟程序:

public class Main {    //每个线程每次运行都申请10M内存    private static final int MEMORY = 1024*1024*10;    private static CountDownLatch sum = new CountDownLatch(46);//限制使用的内存总数,确保可以看到部分存活线程    public static void main(String[] args) throws IOException {        List<Object> objects = new ArrayList<>();        for (int i = 0; i < 10; i++) {            new Thread(()->{                while (sum.getCount()>0) {                    objects.add(new byte[MEMORY]);                    sum.countDown();                    try {                        Thread.sleep(3*1000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }).start();        }        System.in.read();    }}

设置启动参数:

-Xmx512m -Xms128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/dump

启动不久之后就能看到控制台输出 java.lang.OutOfMemoryError: Java heap space

之后用jstack查看,会发现10个线程剩下几个还存活着。

至此,问题来龙去脉已理清,也解决了问题。


阅读全文
0 0
原创粉丝点击