小米训练营-第一次作业

1. 分析问题

​ 给定以下简化版的订单处理代码片段,找出可能导致性能问题和内存泄漏的代码逻辑,并说明原因。

回答:性能问题

image-20250524170904448

​ 1.1 在这当中,使用orderList作为锁监视器,虽然保证了线程安全,但会导致高并发下线程阻塞严重。引文每个订单提交都需要获取锁,导致并发处理能力受限。可以使用如类似CopyOnWriteArrayList的并发容器或者采用CAS + volatile

回答:内存泄漏

image-20250524171533249

image-20250524171549345

​ 1.2 orderListstatisticsMap都是静态变量,和类初始化时被创建,也只有当类被回收才会被回收。作为处理数据的类,该类可能会一直存在,那orderListstatisticsMap就可能会造成内存持续增长。

​ 同时,orderList每满1000的时会创建新的实例对象,而之前的实例对象中还保存着order实例,且被其他类所引用,所以不会被GC回收。

image-20250524173249433

​ 1.3 这里虽然调用shutdown方法关闭线程池,但是在之前的操作中可能会存在异常,或者关闭线程池时出现异常,可能会导致线程池无法被正常关闭,导致内存泄漏。

2. 性能监控与分析

使用jstat监控程序运行状态

image-20250524173600705

使用jvisualvm监控程序运行状态

image-20250524173951060

生成 GC 日志

image-20250524174155399

频繁Full GC的原因

private static List<Order> orderList = new ArrayList<>();
private static Map<String, OrderStatistics> statisticsMap = new HashMap<>();

​ 1. 这两个静态成员变量的,生命周期与类一致,不会被 GC 回收。每个订单都会加入这些集合,所以对象一直存活,无法进入新生代回收流程。当每次Minor GC时,检测到还有对象引用,所以会导致大量对象会晋升到老年代,最终触发 Full GC。

image-20250524174703307

  1. 每创建一个Order实例时,都会调用该构造方法,向每个订单都分配 10KB 内存,10000 个订单就是 100MB。如果这些订单没有及时释放,会快速填满堆内存。导致 Eden 区频繁 Minor GC,对象过早晋升到老年代。

3. 调优实践

设置更加合理的线程池(连接池)的参数

image-20250524175133193

重载Order类的构造方法

image-20250524175332837

调优结果:堆空间占用明显变小!

image-20250524175452479