谈谈项目中主动full gc的一些问题

2020-10-21  本文已影响0人  码农架构

背景

前一段时间在公司一个技术群里,有人在问“有人在线上使用32G内存的服务”。我司线上内存标准配置都是8G的。我就问了一下使用32G内存碰到了啥问题。他的关注点在于一次full gc 时间的长短上。他们面临的问题是上游设置的超时时间太短,一旦他们服务发生full gc,上游服务必然超时。其实很多项目都会面临这个问题。我自己的解决方案就是主动full gc。并且在群里给出了相关的代码。没想到大家对这个主动full gc都很感兴趣,这几天不少人咨询了我这方面的事情。所以就写一篇文章来说说主动full gc。

主动 full gc的标准流程

主动full gc的流程应该如下:

检查是否快满足full gc条件,若到达临界点,进入2;

主动把服务给注册下线;

调用System.gc();

主动把服务注册上线;

如何检查old区域中的使用率呢?

SunJVM提供了一组内存检测接口MemoryPoolMXBean,可以在代码中直接调用这个接口检测各个内存使用情况,具体使用方式可以参考cat中的MemoryInformations。

如何检查是否到达临界点?

一般情况下我们只需要关注old区域即可(有些应用会生成临时类除外)。在使用CMS作为垃圾回收器的时候,我们只要检查old区域的使用率是否快要到达CMSInitiatingOccupancyFraction(并且加上参数UseCMSInitiatingOccupancyOnly,不要让JVM替我们垃圾收集决策)即可。

如何很好的把服务注册下线?

每个公司的基础组件可能都不同,但是一般情况下都会提供这样的功能。比如我司直接在代码中设置状态即可(configStatus.setRuntimeStatus(CustomizedStatus.DEAD);),一行代码即可。但是一定要注意从发起下线到真正下线,中间肯定有时间延迟;而且发起下线的时候服务本身还有一些请求需要处理完成才可以;所以发起线下之后,必须过一段时间才能执行主动GC。

如何很好的执行System.gc()?

你是不是认为这个很简单啊,直接调用System.gc()即可?JDK中代码中有需要直接调用System.gc(),那就看看别人别人怎么写的吧,可以参考DirectByteBuffer中的写法:

大家可以思考一下为什么要在调用System.gc()之后暂时一段时间。

还有要使System.gc()这个生效,必须在jvm中去掉DisableExplicitGC这个参数。

如何优化主动 full gc

控制同时主动full gc机器数

主动full gc中最重要的就是要防止所有机器同时主动full gc,一旦所有机器同时full gc就可能造成上游服务感知不到服务在线了。如果项目中在这方面有比较严的要求,可以使用分布式信号量来保证同时只有特定数目的机器发生full gc;若不需要那么严格,可以让某些机器只在时间末尾为N的发生主动full gc。也就是说可以通过时间末尾来控制最多有1/N台机器主动发生full gc。

降低主动full gc的频率

假设一次主动full gc的的时间分为服务上下线时间 T1和System.gc()执行时间 T2。一般情况下T1>>>T2,T2一般几百ms肯定就可以完成了;T1比较固定,基本上需要几秒甚至十几秒。 这样的话,如果主动降级full gc频率,那么T1的次数很明显下降很多;就算每次主动full gc T2时间上升了对整体停服时间也会降低很多。。

那么调大old区域的大小肯定可以降低full gc频率。那能不能提高old的区域的使用率呢,也就是提高CMSInitiatingOccupancyFraction的配置值。我们知道对于CMS有内存碎片化问题,所以这个参数一般配置的不是很大,比如默认的配置是68%。但是调用System.gc()的时候,执行的不是CMS GC,而是full gc,不存在内存碎片化问题。所以可以通过提高CMSInitiatingOccupancyFraction参数来降低full gc频率。

如何设置好主动full gc的临界点

首先、这个临界点肯定要低于CMSInitiatingOccupancyFraction的配置,这样主动full gc才会生效。这个要结合full gc的频率以及主动full gc时的停服时间还有允许同时主动full gc的机器数目。一般的应用来说我们只要保证cms gc来临的前5分钟有主动full gc的机会就可以了。如果每分钟一次机会,那么4次机会都没有轮上主动full gc,那么我认为要么full gc太频繁了要么full gc时间都太集中了,不管哪种都应该需要优化了。观察一下cms gc前5分钟的old区域使用率设置为临界点即可。

主动 full gc的代码demo

总结

主动full gc还是有不少学问的,比如System.gc()前后的sleep、如何防止所有机器同时发生主动full gc、如何优化主动full gc频率等。

上一篇下一篇

猜你喜欢

热点阅读