gc問題解決:項目高峯期頻繁minorGC,運行一週左右出現oldGC資源不回收

一、現象:

每天早上業務使用高峯期,出現頻繁minorGC,過一段時間後,服務正常。

運行一週左右出現oldGC資源回收效果很小,頻繁出現oldGC,重啓後內存佔用下降。

 

二、查找過程:

 

1.查看內存情況

free -m

內存並未超預期,當oldGC報警時,內存都被佔用。

2.查看gc回收情況

獲取進程號:jps

查看回收情況:jstat -gcutil <進程號> 2000

3.查看堆棧佔用情況

jmap -histo:hive <進程號> > jmap.info

 

三、根據查找過程數據分析:

1.查看jmap.info佔用比較高的那些對象,發現:org.springframework.cloud.sleuth.Span 特別多。正常應該每次請求完,就會釋放span對象,新生代也不會堆積該對象。

2.查找代碼中相關的span代碼,發現如下代碼 

Span tmpSpan = tracer.createSpan(module.getName(), span);

查看源碼,每次新建span的時候都會往spanContextHolder里加入一個不會自動關閉的span

ThreadLocal<SpanContext> CURRENT_SPAN = new NamedThreadLocal<>



static void push(Span span, boolean autoClose) {

   if (isCurrent(span)) {

      return;

   }

   CURRENT_SPAN.set(new SpanContext(span, autoClose));

}

其中使用的ThreadLocal<SpanContext>,由於項目中使用的是線程池,線程會被循環使用。所以這個新創建的span就不釋放。

 

四、解決:

1.找到所有使用到的createSpan的地方,進行顯示的關閉span

tracer.close(tmpSpan);

五、後續:

將這個上線之後,發現span的數量已經從jmap中的前幾名中消失了。

但發現早上業務高峯期查詢比較多的時候,還是會有短時間的minorGC頻繁的報警。再次查找代碼發現:

ThreadPoolExecutor WORK_COUNT_EXECUTOR = new ThreadPoolExecutor(20, 200,

        1L, TimeUnit.HOURS, new LinkedBlockingDeque<>(100), new MyThreadFactory("xxxx"));

使用的線程池最大線程數設置的比較大,就是說在請求量上來的時候,span的數量會是正常情況下的10倍。

 

六、在優化:

1.調低線程池最大線程數的數量,同時,分析業務場景,增大新生代的大小。

 

總結:

1.在使用新的對象的時候,要多閱讀相關使用手冊,正確使用。

2.gc問題是一個持續解決、持續優化的過程,不要要求一次就能完美解決。而且需要持續跟進。

3.遇到內存或cpu問題,莫慌!先收集現場信息,及時恢復服務使用。線下認真分析,相信自己且多和同時討論分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章