缘由
在开放提供的api中不规范使用了线程池,导致java堆中的线程实例不断增加,而线程池没有进行手动关闭的话是会一直保留线程的,故依赖的实例就会一直保留。
看代码
@Path("task")
public class XXXXRest extends BaseResourceApi {
/**
* 线程池大小
*/
public static final int CORE_POOL_SIZE = 5;
/**
* 线程池最大数量
*/
public static final int MAX__POOL_SIZE = 50;
/**
* 线程存活时间 ,单位秒
*/
public static final long KEEP_ALIVE_TIME = 60L;
/**
* 处理定时任务使用线程池
*/
private ExecutorService executorService =
new ThreadPoolExecutor(CORE_POOL_SIZE, MAX__POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
// 重点:在每个rest中创建了一个线程池,而并没有关闭线程池,导致线程池的实例会一直存在,而该实例依赖于rest,故rest实例也会一直存在
}
定位过程
- 使用
jmap -dump:file=data.hprof pid
当前状态下来,使用jvisualvm
查看
或者直接使用jvisualvm
跟踪查看 - 查看该类的实例数量(第二行):
jmap -histo pid | grep 类名
解决方案
将线程池设置为全局公用,这样就不会存在实例一直存在的问题了。
import org.springframework.stereotype.Component;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 公用线程池服务
*/
@Component
public class ExecutorSupport {
private ExecutorService executorService = new ThreadPoolExecutor(50, 200, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.AbortPolicy());
public void execute(Runnable runnable) {
executorService.execute(runnable);
}
public Future submit(Callable callable) {
return executorService.submit(callable);
}
}
@Path("task")
public class XXXXRest extends BaseResourceApi {
@Autowired
private ExecutorSupport executorService;
}
复测
项目运行起来之后,使用jvisualvm
确定实例是否为多个
jvisualvm
的使用
在终端找到jvisualvm
命令,运行在mac环境上,此时会出现画面,选择运行程序的pid连接进去。在监视
一栏有个堆dump
,点击即可产生heap信息。然后在heap信息页面上点击类
,在下方的输入框中输入要查找的类名,即可看见类的实例信息。