问题描述
很多时候我们的系统在使用过程中经常出现卡顿的情况,或者请求变慢(延迟高)等等情况,然后运维人员跑来给你说写的什么垃圾代码,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); }); } /** * 模拟从数据库读