场景
- 在EC2上跑了一个归档程序
- 归档程序从S3上拉取小文件,然后按照数据的key进行划分,存储到新文件中
- 当文件数超过300W时,CPU占用超过70%,大概率超过80%
- 查看jvisualvm后发现,大量CPU耗费在GC上,并且GC频率特别频繁
- 新生代GC与老年代GC的触发频率达到 1:10
-
追踪步骤
先去dump了内存快照
- 然后使用MAT的内存泄漏分析,找到了占空间最大的对象:
com.amazonaws.services.s3.model.ListObjectsV2Result
- 进一步追踪代码,发现程序在一开始通过循环调用ListObjects,一次性获取了300W文件的信息
- 而且在获取了ListObjectsV2Result后,将result加入list,然后把list作为数据源传入线程池,list在线程池中并未被remove过,全程一直引用这所有的Result对象
- 大量Result对象占据了内存
并且Result对象中除了线程池需要的数据,还有一堆请求响应状态,白白占用空间
优化方案
放弃list,改用阻塞队列。不再一次性获取所有文件信息,而是等线程池消耗一批后,再获取下一批文件信息
- 线程池处理完一批数据后,就会释放掉,不再引用
队列中不再存放原始的Result对象,改为自己封装的更轻量的Task对象
优化结果
归档时间从两天也跑不完,到半个小时就可以跑完
- CPU占用从70~80%,降到30%