问题描述
很多时候我们的系统在使用过程中经常出现卡顿的情况,或者请求变慢(延迟高)等等情况,然后运维人员跑来给你说写的什么垃圾代码,CPU 飙升,经常 100%,内存飙升。
如果你不知道怎么定位问题,觉得自己代码也找不出什么问题,那就说重启试试咯!!!
当然了,这种处理方式可以解决,但并不能从根本上解决问题,试想一下如果对于高并发的系统,你重启项目肯定是不行的,所以我们要找到问题的源头,因为我们程序都是跑在 JVM 里面的,所以上面的 2 种情况很可能是我们的代码出现了问题,比如常见的死循环,递归等等,当然这种就是比较明显的错误。大多数情况下我们能察觉出来,而对于比较难以察觉的代码问题,则需要我们在 JVM 的层面去解决,也就是所谓的 JVM 调优。
问题如下:
-
系统 CPU 经常 100%,如何调优?
-
系统内存飙升,如何定位问题?
当然,有人会说,加内存,加服务器等等,我们要理解调优的目的,其目的是为了利用现有的内存来获取高吞吐量或者低延迟,说白了就是花更少的钱做更多的事。
场景模拟
之前在公司做过一个差旅系统(类似携程,但针对的是企业用户),大概意思是年终要定时统计年终的差旅费用,包括机票,酒店费用报销等等,把数据从 MySQL 中取出来计算汇总,如下:
大概步骤:
-
从数据库中批量读取用户信息;
-
利用线程池多线程执行任务计算。
代码如下:
/**
* VM参数: -XX:+PrintGC -Xms200M -Xmx200M
* GC调优---生产服务器推荐开启(默认是关闭的)
* -XX:+HeapDumpOnOutOfMemoryError
*/
public class FullGCProblem {
//线程池
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50, new ThreadPoolExecutor.DiscardOldestPolicy());
public static void main(String[] args) throws Exception {
//50个线程
executor.setMaximumPoolSize(50);
while (true) {
calc();
// 休眠 100ms 模拟计算耗时
Thread.sleep(100);
}
}
/**
* 模拟多线程执行任务计算
*/
private static void calc() {
List<UserInfo> taskList = getAllCardInfo();
taskList.forEach(userInfo -> {
executor.scheduleWithFixedDelay(() -> {
userInfo.other();
}, 2, 3, TimeUnit.SECONDS);
});