內存溢出的三種類型:
- 第一種OutOfMemoryError: PermGen space,發生這種問題的原意是程序中使用了大量的jar或class
- 第二種OutOfMemoryError: Java heap space,發生這種問題的原因是java虛擬機創建的對象太多
- 第三種OutOfMemoryError:unable to create new native thread,創建線程數量太多,占用內存過大
初步分析:
初步懷疑是線程創建太多導致,使用jstack 線程號 > /tmp/oom.log將應用的線程信息打印出來。查看oom.log,發現大量線程處於Runnable狀態,基本可以確認是線程創建太多了。
代碼分析:
1.出問題的微服務是日志寫庫服務,對比日志,鎖定在writeLog方法上,wirteLog方法使用spring-@Async注解,寫庫操作采用的是異步寫入方式。
2.之前沒有對@Async注解深入研究過,只是知道可以自定義內部線程池,經查看,日志寫庫服務並未自定義異步配置,使用的是spring-@Async默認異步配置
3.網上提到@Async默認異步配置使用的是SimpleAsyncTaskExecutor,該線程池默認來一個任務創建一個線程,在壓測情況下,會有大量寫庫請求進入日志寫庫服務,這時就會不斷創建大量線程,極有可能壓爆服務器內存。
最終解決辦法:
1.自定義線程池,使用LinkedBlockingQueue阻塞隊列來限定線程池的上限
2.定義拒絕策略,如果隊列滿了,則拒絕處理該任務,打印日志,代碼如下: