现象描述在线运行了几个月的服务突然爆OOM,重启后隔几天又爆OOM。我把内存从512M增加到2G。平静了一周之后,OOM再次爆发。这只能是代码问题!!!在排查过程中,由于网上没有jvmdump文件,只能手动查看代码。疑点一:select*fromtableSQL全量查询,在数据量足够大的情况下会OOM。但是这个项目中并没有这个查询,只能继续调查了。疑点二:错误的操作集是出于商业目的。筛选时需要将dedicated和publichandler都放入候选集中,所以出现如下错误代码。公共类管理器{privateListcommonHandlers=newArrayList<>();privateMap>handlers=newHashmap<>();publicListdispatch(Condition条件){ListalternativeHandlers=handlers.get(condition.getType());alternativeHandlers.addAll(CommonHandlers);//常用的handlers也需要添加为alternatives//其他过滤操作handlers=alternativeHandlers.stream().filter(.......).....returnhandlers;}}每次调用dispatch方法,都会修改handlers中的List,导致数据越来越多。因为数据量小,业务逻辑BUG会在短时间内爆发。同时,这种BUG理论上会导致OOM。但是调查发现这个项目并没有这个操作,只能继续调查了。疑点3:线程池Java8HotSpotJVM的GC通过可达性分析判断垃圾,可达性的起点是GCRoot有StackLocal、Class、JNILocal、JNIGlobal、LiveThread,其中前两个前两步已经排除了嫌疑,本项目不涉及JNI相关操作,所以LiveThread留作可能。根据前两步可以看出,线程引用的对象是没有OOM的可能的,所以唯一的可能就是线程本身一直在增加,最终导致OOM。根据这个思路,找到了如下可疑代码。公共类处理程序{privateThreadPoolExecutorexecutor=newThreadPoolExecutor(.....);publicvoidhandle(Objectarg){executor.submit(()->{//实际处理的业务逻辑...});这段代码的问题在于Handler本身会被Manager周期性的刷新,每次刷新都会构建一个新的Handler并丢弃旧的。但是旧的Handler中的线程池并没有关闭,线程池中的线程会一直存活。随着时间的推移,越来越多的废弃线程最终会导致OOM。锁定违规者:需要更新的对象引用的线程池。当创建对象的新实例时,并没有关闭旧实例的线程池,而是直接丢弃。解决方案:在丢弃旧的Handler之前手动关闭()相关问题?单例对象引用的线程池或线程在主线程退出时并没有关闭。为什么不会导致OOM?一个Java应用程序独占一个JVM进程。当主线程退出时,进程就会退出,进程中的所有线程也会退出。退出后内存就会释放,当然不会有内存问题。没有关闭的线程会发生什么?会一直运行直到关闭,或者直到进程退出子线程才会退出