做开发这么多年了,经常听到很多干开发的同学说,“GC是全自动的,所以不需要操心GC”。
这句话一半真,一半假。“GC是全自动的”这句话是对的,jvm的GC在目前的java平台上都是全自动的,包括Oracle JDK,OpenJDK和其他JDK发行版,还包含移动端安卓上的ART虚拟机;但“GC不需要操心”是错的,GC是全自动的,但是它是有代价的,有时候,这个代价还会非常昂贵.
表现不佳的垃圾回收会带来以下几种问题:
1. 损害用户体验以及服务的SLO
-
浪费服务器成本
-
使整个应用面临风险
接下来我们分别讨论这三个点.
1. 损害用户体验以及服务的SLA
当GC在后台自动运行时,整个应用都会暂停以便GC标记无用对象以及清理这些对象。在这个暂停周期内,所有的程序操作都会暂停、冻结。根据配置的GC算法不同,这个暂停时间可能从 ms到s,甚至到分钟级别。因为以上情况,GC会影响应用的SLA(service level agreement,服务等级保证,一般互联网公司的定义是99.99%的请求在50ms以内返回).根据亚马逊的调研,网站打开时间增加100ms,就会导致销量下降1%,对于亚马逊这个量级,,1%就代表着60亿美元的成交额(亚马逊2021年的总交易额是6000亿美元).
移动端的问题也一样,频繁的gc会使app的行为断断续续(动画不连贯,卡顿,声音卡顿),这也是会非常伤害用户体验的行为。
2. 浪费服务器成本
GC会消耗非常多的cpu资源,每个应用的内存中都有成千上万的对象在创建、分配和回收。GC会定期检查内存中的所有对象,确认它们是否还在使用?是否还有引用关系?这些引用是否还有效?GC收集这些信息和计算会非常消耗CPU。
根据笔者的经验,一般最早饱和的资源就是内存,其次才是CPU、硬盘和带宽。使用的内存越多,就会导致服务器成本越高;GC频率越高,使用的CPU也就越多,也会导致服务器成本上升。
一个真实线上应用的GC消耗CPU的情况,可以看到GC线程占用了25%的cpu。
3. 使整个应用面临风险
有时GC事件会使应用暂停几秒钟到几分钟,有时GC事件会重复运行。当应用执行GC时,用户的请求将全部被暂停/挂起。如果应用陷入到无限的GC死亡循环,就不得不重启应用才能缓解。这些行为对应用的伤害都非常大。
为了使用户获得极佳的体验,为了减少服务器的账单,提高应用的可用性,应用的开发人员/架构师必须研究和优化内存/应用/GC设置。
笔者所在的公司,通过基于gc的优化,成功节约了数万G的内存。现在整个大环境都在提倡降本增效,我建议大家可以尝试从jvm内存角度来做一些优化。