作者 | 慕鱼
来源 | 凌云时刻(微信号:linuxpk)
WHY:为什么要回收?
Java虚拟机的内存资源有限,在不断运行分配内存过程中,如果没有垃圾回收机制,会逐渐消耗并最终导致内存资源不足。Java 虚拟机需要回收的垃圾,就是那些不再被使用的内存。
ECS云服务器中宿主机(host)的计算资源有限,在不断为云服务分类资源过程中,如果没有资源回收与释放,同样会导致资源消耗完。ECS云服务器中,宿主机需要回收的资源包含云服务器运行占用的所有资源,如内存、vCPU、IP地址、磁盘存储空间、网络带宽、GPU、FPGA等资源。
WHAT:回收什么?
- Java虚拟机垃圾回收
如何确定内存不再被使用了呢?
- 引用计数法
这个算法的实现是,给对象中添加一个引用计数器,每当一个地方引用这个对象时,计数器值+1;当引用失效时,计数器值-1。任何时刻计数值为0的对象就是不可能再被使用的。这种算法使用场景很多,但是,Java中却没有使用这种算法,因为这种算法很难解决对象之间相互引用的情况。
- 可达性分析法
这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。
那么问题又来了,如何选取GCRoots对象呢?
在Java语言中,可以作为GCRoots的对象包括下面几种:
- 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象
- 方法区中的类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(Native方法)引用的对象
由图可知,obj8、obj9、obj10都没有到GCRoots对象的引用链,即便obj9和obj10之间有引用链,他们还是会被当成垃圾处理,可以进行回收。
- ECS资源整理
目前业务允许回收的场景包含以下三点:
- 用户主动释放的资源,或者用户按量收费停机资源【按量收费与否不保证库存预留,因此资源可被回收】
- 用户购买的竞价实例,当资源紧张的时候,可以回收资源给其他类型产品使用。
- 用户购买的预付费产品,欠费一定时间后,系统便可以对该ECS产品进行资源回收。
HOW:怎么回收?
- Java虚拟机垃圾回收
在确定了哪些垃圾可以被回收后,垃圾收集器要做的事情就是开始进行垃圾回收,但是这里面涉及到一个问题是:如何高效地进行垃圾回收?
由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器。
- Mark-Sweep(标记—清除)算法
分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。这种算法的不足主要体现在效率和空间,从效率的角度讲,标记和清除两个过程的效率都不高;从空间的角度讲,标记清除后会产生大量不连续的内存碎片, 内存碎片太多可能会导致以后程序运行过程中在需要分配较大对象时,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作。
- Copying(复制)算法
为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。
- Mark-Compact(标记—整理)算法
为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
- Generational Collection(分代收集)算法
现代商用虚拟机基本都采用分代收集算法来进行垃圾回收。这种算法没什么特别的,无非是上面内容的结合罢了,根据对象的生命周期的不同将内存划分为几块,然后根据各块的特点采用最适当的收集算法。大批对象死去、少量对象存活的(新生代),使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担保的(老年代),采用标记—清理算法或者标记—整理算法。
- ECS资源整理
- Mark-Sweep(标记—清除)算法
这种算法是ECS最开始回收的雏形,用户提交释放请求或者满足释放调节的ECS服务器,则进行实时的资源回收与释放。显而易见,这种算法会导致宿主机存在大量的碎片。当大规格实例创建时,导致无法找到足够资源的宿主机。
- Mark-Compact(标记—整理)算法
借鉴Java内存对应回收的方式,ECS整理技术经历了两个里程碑的突破。
第一里程碑:对于需要进行资源整理的实例进行标记,等待启动或重启迁移,即用户在启动或重启服务器的时候,ECS管控系统会将实例重新调度到更合适的宿主机上。
第二里程碑:热迁移 这一划时代功能上线后,实例理论上可以在任何时间,进行宿主机切换。而不必局限于启动或重启时机。因此,可以根据ECS云服务器的负载,功耗,碎片等指标进行实时整理。
- Generational Collection(分代收集)算法
将集群的中的若干宿主机根据其生产云服务器生命周期的长度进行分组,分组的个数、组内的数量可以根据每个集群的不同情况进行设定。
如图所示,可以将宿主机分为三组,分别为小时组、按天组和按月组,数量分别为n、k、i个。根据不同的集群使用情况,可以增加分钟组、按年组等等。每个组内宿主机的数量,根据生产云服务器不同需要可以动态扩容、缩减或者转换。n、k、i的数量也是可以动态变化和调整的。
- 用户发起创建云服务器请求,服务管理控制中心获取用户创建请求,例如包月、包年、包周、按量等类型,根据评估算法,将其转化为生命周期预估因子。生命周期预估因子传递给宿主机分组管理系统进行解析,得到对应时长的宿主机分组。进而分配宿主机的地址,最后,交给宿主机进行生产。
- 用户发起的释放请求,直接由物理服务器进行释放。
- 宿主机分组管理系统会检查组内每个宿主机上的云服务器,如果云服务器存活时长满足迁移条件,如果不满足,则进行云服务器生命周期因子叠加,如果满足则进行云服务器迁移。
- 云服务器迁移分为两类,满足组间间迁移条件,则直接进行跨组的迁移,否则进行组内宿主机调度迁移。
总结
本文简单谈了一下Java垃圾回收与ECS资源整理算法的比较,两者有很多相通的解决算法。但实际上,ECS资源整理需要考虑的情况比本文谈到的更复杂,比如:
- 云服务器的负载情况
- 云服务器的聚合与打散情况
- 宿主机的健康状况
- 宿主机机架的功耗情况
- 宿主机机型
- 网络及磁盘负载情况
调度系统的整理调度优化是一个庞大复杂又具有划时代意义的课题,ECS控制系统正在多方面合作不断优化这一问题,相信未来的ECS管控调度系统会像“后羿射日”般精准!
参考文献:ava垃圾回收(GC)机制详解