作者:萌新求学 | 来源:互联网 | 2023-07-10 14:48
篇首语:本文由编程笔记#小编为大家整理,主要介绍了从ExecutorService的内存溢出谈谈线程池相关的知识,希望对你有一定的参考价值。 之前写的一个Sq
篇首语:本文由编程笔记#小编为大家整理,主要介绍了从ExecutorService的内存溢出谈谈线程池相关的知识,希望对你有一定的参考价值。
之前写的一个Sql转发应用出现了内存溢出问题,经过排查发现是ExecutorService没有正确的进行关闭。
正常来说如果我们将ExecutorService设计成一个静态变量,那么通常我们是不用去管理其是否关闭的,我们只需要对其本身的线程进行维护操作,ExecutorService对象不用我们显示的进行维护操作。但是维护静态线程池对象的不足之处在于,不好去界定池量级的大小,如果太小会导致线程过多的时候线程运行得不到保证,如果过大则通常线程执行时是对内存的一种浪费。所以在调用频率不高的时候,我们往往会在一次调用的时候创建一个ExecutorService,用这个来进行线程池的管理,那么这里就出现了我们遇到的问题,那就是这个ExecutorService其实是需要手动进行关闭的。
上图中ExecutorService就没有正确的关闭。
为什么ExecutorService不会自己进行回收呢,我们通过对其API的阅读可以得到答案,若是想要ExecutorService完全关闭需要同时满足三个条件:
1.线程池中没有正在执行的task任务
2.线程池中没有等待执行的task任务
3.无法再提交任务到ExecutorService
显然,如果我们不进行手动关闭的话,第三个条件将无法满足,导致无法其内存无法被回收,而下一次的调用又继续进行了线程池的创建,这样内存迅速的上升,最后导致了OOM。
那么知道了问题的原因,解决方法其实就比较简单了,我们在使用完之后再调用一个shutdown或者shutdownNow,让线程池状态变成不再能接受线程任务就行了。
但是这个时候就有了新的问题,如果某个线程阻塞,一直无法关闭,shutdown方法也不能完成自己的使命,但是如果使用shutdownNow方法的话,就会出现关闭的时候还有线程任务没有完成。这个时候要怎么合理的进行线程任务的关闭呢。
这个时候我们可以设定一个最大超时时间,如果执行shutdown之后经过超时时间还有任务没有结束,那我们就用shutdownNow方法来帮他们上路。