小米训练营-第一次作业
小米训练营-第一次作业
1. 分析问题
给定以下简化版的订单处理代码片段,找出可能导致性能问题和内存泄漏的代码逻辑,并说明原因。
回答:性能问题
1.1 在这当中,使用orderList
作为锁监视器,虽然保证了线程安全,但会导致高并发下线程阻塞严重。引文每个订单提交都需要获取锁,导致并发处理能力受限。可以使用如类似CopyOnWriteArrayList
的并发容器或者采用CAS + volatile
。
回答:内存泄漏
1.2 orderList
和statisticsMap
都是静态变量,和类初始化时被创建,也只有当类被回收才会被回收。作为处理数据的类,该类可能会一直存在,那orderList
和statisticsMap
就可能会造成内存持续增长。
同时,orderList
每满1000的时会创建新的实例对象,而之前的实例对象中还保存着order
实例,且被其他类所引用,所以不会被GC回收。
1.3 这里虽然调用shutdown
方法关闭线程池,但是在之前的操作中可能会存在异常,或者关闭线程池时出现异常,可能会导致线程池无法被正常关闭,导致内存泄漏。
2. 性能监控与分析
使用jstat监控程序运行状态
使用jvisualvm监控程序运行状态
生成 GC 日志
频繁Full GC的原因
private static List<Order> orderList = new ArrayList<>(); |
1. 这两个静态成员变量的,生命周期与类一致,不会被 GC 回收。每个订单都会加入这些集合,所以对象一直存活,无法进入新生代回收流程。当每次Minor GC时,检测到还有对象引用,所以会导致大量对象会晋升到老年代,最终触发 Full GC。
- 每创建一个
Order
实例时,都会调用该构造方法,向每个订单都分配 10KB 内存,10000 个订单就是 100MB。如果这些订单没有及时释放,会快速填满堆内存。导致Eden
区频繁 Minor GC,对象过早晋升到老年代。
3. 调优实践
设置更加合理的线程池(连接池)的参数
重载Order类的构造方法
调优结果:堆空间占用明显变小!
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Yomigaeri的博客!