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问题,莫慌!先收集现场信息,及时恢复服务使用。线下认真分析,相信自己且多和同时讨论分析。

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