GC算法介绍

在这呢,我们去介绍一下GC算法相关的一些内容。

首先呢我们在说算法之前先把GC来做一个定义,对于我们来说,GC呢就可以理解为垃圾回收机制的一个简写,当GC工作的时候他可以帮我们找到内存当中的一些垃圾对象,然后呢对于这一空间可以进行释放并且呢还可以进行回收,分配之后方便我们后续的代码呢继续去使用。

那么听到这里我们就不禁的想问,什么样的东西在GC里边可以被当做是垃圾看待呢?在这里我们给出了两种小的标准。

第一种呢,我们从程序需求的角度来考虑,如果说我们某一个数据在使用完成之后上下文里边不再需要去用到他了。我们就可以把他当做是垃圾来看待。

例如我们下面代码当中的name,那当函数调用完成以后,在这里呢我们其实已经不再需要使用name了,因此从我们需求的角度来考虑,他应该呢是被当做垃圾进行回收的。至于说到底有没有被回收呢,我们现在不做讨论。

function func() {
    name = 'yd';
    return `${name} is a coder`
}

func()

第二种情况呢,就是从我们当前程序运行过程中,那么这个变量还能否被引用到的一个角度上去考虑,例如我们下方这个代码当中,依然是在函数内部去放置一个name,不过这次我们去加上了一个声明变量的关键字。

有了这样一个关键字以后呢,当我们函数调用结束之后,那么我们在外部的空间当中就不能够再访问到这个name了。

所以当我们找不到他的时候,其实他也可以算作是一种垃圾。

function func() {
    const name = 'yd';
    return `${name} is a coder`
}

func()

所以说这块的话呢,我们大致就知道了,在GC里边什么样的内容可以被当做是垃圾来进行对待。

那说完了GC以后呢我们就可以来看一下,什么是GC算法。

在这里呢我们已经知道了GC呢其实就是一种机制,它里面的垃圾回收器可以去完成具体的回收工作,而工作的内容本质呢就是查找垃圾释放空间并且回收空间。

所以说在这个过程当中呢就会有这个几个行为。第一如何去查找空间,第二我们在释放空间的时候又该怎样去释放,那么回收空间的过程中我们又如何去进行分配。

所以这样一系列的过程里面必然有不同的方式,所以说这个GC的算法我们就可以理解为是上述的垃圾回收器在工作过程中所遵循的一些规则,好比呢就是一些数学的计算公式,那这就是我们对于GC算法的一个定义。

知道了什么是GC算法之后那么我们就来说一些常见的GC算法名称。在这里面呢我们会去介绍到这样几个GC算法。

第一,引用计数,可以呢通过一个数字来判断当前的这样一个对象是不是一个垃圾,后续呢我们会讲到。

第二,标记清除,可以呢在GC工作的时候去给到那些个活动对象呢添加上一个标记,来判断他是否是一个垃圾。

第三,标记整理,与标记清除呢其实很类似,只不过呢在我们后续回收过程中,他可以去做出一些不一样的事情,具体呢,我们后续会说。

最后一个,分代回收,将来在V8当中呢我们会用到这样一个回收机制。

那这块就是关于我们GC算法所给出的一些内容介绍。

引用计数算法实现原理

接下来我们去看一下关于引用计数算法实现原理相关的一些内容介绍。

首先针对于引用计数算法来说,他的核心思想其实呢就是在内部去通过一个引用计数器来维护当前对象的引用数,从而呢去判断该对象的引用数值呢是否为0,来决定他是不是一个垃圾对象。

当这个数值呢为0的时候,那么GC呢就开始工作,将其所在的一个对象空间呢进行回收和释放再使用。

那么在这里呢,我们提到了一个名词。叫引用计数器,关于他呢我们需要有一个小小的印象。

因为相对于其他的GC算法来说,也正是由于引用计数器的存在那么导致了引用计数呢在执行效率上可能与其它的GC算法呢有所差别。

那么这个说完以后呢我们还需要再思考一下,我们引用的这样一个数值什么时候会发生改变呢?所以在这里呢,他给出的一个规则是这样的。

当,某一个对象他的引用关系去发生改变的时候,那么引用计数器呢就会主动的去修改当前这个对象所对应的引用数值。

那什么叫做引用关系发生改变呢?例如说我们的代码里面现在有一个对象空间,目前来说呢有一个变量名指向他,那么这个时候就把数值+1,那么如果说在这个时候又多了一个对象还指向他那么我们就把他再+1,那么如果是减小的情况下呢我们就-1就行了。

当我们发现这样一个引用数字为0的时候,那么GC呢就会立即工作,然后将当前的对象空间呢进行回收。

那么说完这样的一块原理之后呢,我们再通过一些简单的代码来说明一下这个引用关系发生改变的一种情况。

首先在这里我们定义几个简单的user变量,我们采用的是const关键字,然后我们去设置一下,把他呢作为一个普通的对象,那这里边呢,我们采用的是数值写起来呢比较方便。

完成这些操作以后呢我们再去定义一个变量,然后这块呢我们给到的是一个数组,然后在数组的里边呢我们去存放一下上述几个对象当中的age属性值, 所里这里边我们就变成了user1.age, user1.age, user1.age。

做完这个以后我们再来定义一个函数,在函数体内我们还是定义几个变量,这里我们定义成数值num1和num2,注意这里是没有const的。

这个时候我们再外层去调用这样一个函数, 那写完这段代码以后我们去发现这个地方并没有什么输出,所以我们只是用它来分析一下当前这里面所谓的叫引用数值的变更。

const user1 = {age: 11};
const user2 = {age: 22};
const user3 = {age: 33};

const nameList = [user1.age, user2.age, user3.age,];

function fn() {
    num1 = 1;
    num2 = 2;
}

fn();

首先呢我们从全局的角度去考虑,我们会发现window的下边是可以直接找到user1,user2,user3以及我们的nameList, 我们从变量这个角度出发。

同时呢我们现在在fn这样一个函数里面我们定义的num1和num2呢由于我们没有去设置这样一个关键字,所以他同样是被挂载在我们当前这样一个window对象下的。

所以这个时候对于这些变量来说他们的引用计数肯定都不是0,然后紧接着我们去做一些修改。

比如我们在函数内直接把num1和num2呢我们去加上一个关键字的声明,那么加上了这个关键字的声明以后,就意味着我们当前这个num1和num2呢他只能在,这样的一个作用域内去起效果。

const user1 = {age: 11};
const user2 = {age: 22};
const user3 = {age: 33};

const nameList = [user1.age, user2.age, user3.age,];

function fn() {
    const num1 = 1;
    const num2 = 2;
}

fn();

所以,一旦当我们的函数调用执行结束之后,那我们从外部全局的地方去出发就不能够再找到num1和num2了,那么这个时候呢,num1和num2他们身上的一个引用计数呢就会回到0。

此时此刻呢, 只要是0的情况下,GC呢就会立即开始工作,将num1和num2呢当做垃圾去进行一个对象回收。也就是说这个时候函数执行完成以后他们内部所在的一个内存空间就会被回收掉。

那么紧接着我们再来看一下其他的,比如说user1,user2,user3以及呢nameList。

由于在这个地方呢,我们的userList,他的里面呢刚好都指向了我们上述三个对象空间,所以,当我们脚本即使执行完一遍以后他回头一看user1,user2,user3他们里边的空间呢其实都还被人引用着。

所以此时的引用计数器呢就不是0,那么这个时候就不会被当做垃圾呢去进行回收。

那这块呢就是关于我们的引用计数这样一个算法在实现过程中他所遵循的一些基本原理。

简单的总结一下呢其实就是靠着我们当前对象身上的一个引用计数的数值来判断是否为0,从而呢来决定他是不是一个垃圾对象,那这地方我们就说完了。

引用计数优缺点

在这里呢我们来看一下关于引用计数算法的优缺点介绍。

首先呢我们来看一下引用计数算法的优点,这里我们总结出两条,第一就是引用计数这样一个规则呢会在发现垃圾的时候立即进行回收,因为他可以根据当前这样一个引用数是否为0来决定这个对象呢是不是一个垃圾。如果她找到了,那么这个时候呢就可以立即进行释放。

第二呢就是引用计数算法可以最大限度的去减少程序的一个暂停,这句话的意思是怎么解释呢?

我们可以这样取简单的描述一下,我们的应用程序在执行的过程当中,必然呢会对内存进行消耗。而我们当前的执行平台他的内存肯定是有上限的,所以内存肯定有占满的时候。

不过由于引用计数算法呢他是时刻监控着内存一些引用值为0的对象,所以我们就可以认为,举一个极端的情况就是,当他发现这个内存即将爆满的时候,那么引用计数呢就会立马去找到那些个数值为0的对象空间然后对其呢进行释放。

所以这样呢就保证了我们当前这样一个内存呢是不会有占满的时候,这也就是所谓的减少我们程序暂停的一个说法。

然后我们去看一下引用计数的一些缺点,这里同样呢我们是给出了两条说明。

第一个呢就是引用计数算法呢是没有办法将那些循环引用的对象进行空间回收的,这个呢我们会以具体的代码来演示一下。

第二个呢就是引用计数算法呢他所消耗的时间呢会更大一些,这块是为什么呢?

因为我们当前的引用计数,他需要去维护一个数值的变化,所以在这种情况下他要时刻的去监控着当前对象的一个引用数值是否呢需要修改。

那么本身来说我们这个对象他的一个数值的修改就需要消耗时间,那如果说我们这个内存里边有更多的对象需要修改,那么这个时间呢就会显得更大。

所以说这块呢是相对于其他的GC算法来说,我们会觉得引用计数算法他的一个时间开销会更大一些。

那么这块就死关于引用计数优缺点的一个简单说明,那么接下来我们就去回到代码当中,利用一个小的代码片段来演示一下,什么叫做循环引用的对象。

那这里我们去定义一个普通的函数,我们直接呢还是使用fn来表示一下,然后我们在函数体的内部呢,我们先去定义两个变量,这块呢我们先去直接把他设置为对象obj1和obj2。

紧接着在下边我们对他们做一个赋值操作,我们让obj1下面有一个name属性然后指向我们当前的obj2,然后紧接着呢我们再让obj2有一个属性去指向我们obj1。

那这个时候我们再最后的地方,再去通过一个return返回一个普通的字符,这块呢,没有什么实际的意义只是呢来做一个测试。

接着在最外层我们去调用一下这个函数。

function fn() {
    const obj1 = {};
    const obj2 = {};

    obj1.name = obj2;
    obj2.name = obj1;

    return 'yd is a coder';
}

那么接下来我们就去分析一下,还是一样的道理,我们这个函数呢在执行结束以后,那么他内部所在的这样一个空间呢,肯定需要有涉及到空间回收的情况。

比如说我们的obj1和obj2,因为在全局的地方呢其实我们已经不再去指向他了,所以说这个时候呢,他的引用计数呢应该是为0的。

但是呢,这个时候会有一个问题,什么问题呢?在里边我们会发现,当我们想要去来找GC把obj1删除的时候,他会告诉我obj2呢现在有一个属性是指向我们obj1的。

所以换句话讲就是虽然按照之前的规则,我们再全局的作用域下,找不到了obj1和obj2,但是由于他们两者之间在这样的一个作用域范围内明显还有着一个互相的指引关系。

所以在这种情况下他们身上的引用计数器当中的数值并不是为0的,那么这个时候引用计数下的算法GC就没有办法再将这样的两个空间进行回收了。

从而呢也就造成了我们内存空间的一个浪费,这呢就是所谓的叫做对象之间的一个循环引用。

那这一块呢也是我们当前引用计数算法所面临到的一个问题。

那这块我们关于引用计数算法的优缺点我们就讲到这里。

标记清除算法实现原理

在这里呢我们来看一下标记清除算法的实现原理,相对于之前我们提到过的引用计数来说这个GC算法呢他的原理实现更加简单,而且还能解决一些相应的问题。

那么在后续的V8当中他会被大量的使用到,所以在这呢我们就先去看一下标记清除算法的实现原理。

对于标记清除算法来说,他的核心思想就是,将整个垃圾回收操作呢,分成两个阶段,第一个阶段呢他会去遍历所有的对象然后找到这些活动对象,然后进行标记的操作。

那这里的活动就像呢就跟我们之前所提到的可达对象是一个道理,第一个操作完成以后,接下来呢会进入到第二个阶段,在第二个阶段里呢仍然是会去遍历所有的对象,然后把那些个身上没有标记的对象呢进行清除。

同时需要注意的就是,在第二个阶段当中,他也会把第一个阶段所设置的标记呢给抹掉,便于我们GC呢,下次还能够去正常的工作。

那这样一来的话呢,他就可以去通过两次的遍历行为把我们当前这样一个垃圾空间去进行回收,然后最终呢再交给我们相应的这样一个空闲列表进行维护,后续我们的程序代码呢就可以实现使用了。

所以,这就是我们当前标记清除算法的一个基本原理,其实呢就是两个操作,第一就是标记,第二就是做清除。

那么为了方便这样一个理解,我们这里进行一个举例说明。

首先我们先简单的去声明一下,在全局global这个地方我们可以去找到A,B,C这样的三个可达对象,那么找到这三个可达对象之后,我们会发现他的下边还会有一些子引用,所以这就是标记清除算法强大的地方。

如果我们发现他的下边会有孩子,甚至于说孩子下边还有孩子,那么这个时候他会去用递归的方式继续呢去寻找那些可达的对象,比如说D,E分别是A和C的子引用,也会被进行一个可达的标记。

那么这个时候我们还有两个变量a1和b1,他们是在函数内部的局部作用域,而我们当前这样一个局部作用域执行完成以后呢这样一个空间就被回收了。

const A = {};

function fn1() {
    const D = 1;
    A.D = D;
}

fn1();

const B;

const C = {};

function fn2() {
    const E = 2;
    A.E = E;
}

fn2();

function fn3() {
    const a1 = 3;
    const b1 = 4;
}

fn3();

所以从我们当前global这样一个链条下呢,我们是找不到a1和b1的,那么这个时候我们的GC机制呢,就会认为他是一个垃圾对象,没有去给他做标记,那么最终呢,在我们GC去工作的时候就会找到a1和b1然后直接把他们回收掉。

那这块就是标记清除所谓的标记阶段和清除阶段,要做的事情。简单的整理一下就是分成两个步骤。

在第一个阶段当中,我们要去找到所有可达对象,如果说在这里呢涉及到了我们这样的一个引用的一个层次关系,那么他会递归的进行查找,就像我们的global找A再找D这样的一个过程。

那么找完以后他会将这些可达对象呢都进行标记。那么标记完成以后呢会进行第二个阶段,然后呢开始去做清除,找到那些没有去做标记的对象,同时呢还会将我们之前第一次呢所做的标记呢,也给他清除掉。

那这样我们就完成了一次垃圾的回收,同事呢我们还要留意,最终呢他还会去把回收的空间呢直接放在我们当前的一个叫做空闲列表上面。方便我们后续的程序呢可以直接在这呢去申请空间使用。

那这块就是关于标记清除算法的实现原理。

标记清除算法优缺点

在这里呢我们来看一下关于标记清除算法优缺点的介绍。

那么作为一个GC算法的出现,他依然是做不到十全十美的,所以跟我们的引用计数一样,他也存在着自己的一些优势和缺点的地方。

首先,我们来看一下相对于引用计数来说标记清除具有一个最大的优点,就是他可以去解决我们之前对象循环引用的一个回收操作。

简单说明一下,我们在写代码的时候可能会直接在全局的地方定义A,B,C这样的一个可达对象,但是呢我们也会去有一些,函数的一个局部作用域,比如我们当前在一个函数内定义了a1和b1,而且让他们呢互相引用。

const A = {};

function fn1() {
    const D = 1;
    A.D = D;
}

fn1();

const B;

const C = {};

function fn2() {
    const E = 2;
    A.E = E;
}

fn2();

function fn3() {
    const a1 = 3;
    const b1 = 4;
}

fn3();

那么对于这种函数的调用呢在结束之后必然呢要去释放他们内部的空间,所以在这种情况下,一旦当某一个函数调用结束之后呢,他局部空间中的变量就失去了与我们当前全局global在作用域上的一个链接。

所以这个时候a1和b1呢,在我们全局的global根下边就没有办法再访问到了,所以这个时候呢,他就是一个不可达的对象。

那么不可达对象在做标记阶段的时候就不能够完成标记,那么接下来在第二个阶段,我们当前去回收的时候,就直接找到这些没有标记的对象,把他们内部的空间进行释放。

这是标记清除呢可以做到的事情,但是在我们的引用计数里面,虽然我们当前这个函数调用结束以后,他呢也没有办法在全局的地方呢去进行访问。

可是呢,由于我们当前判断的标准是引用数字是否为0,所以在这种情况下,他就没有办法去释放a1和b1的空间,这就是我们当前标记清除算法的一个最大优点。就是相对于我们当前的引用计数算法来说的。

那同时呢我们的标记清除算法也会有他自己的一些缺点。这个地方我们简单举例来进行说明。

比如我们模拟一个内存的存储情况,我们当前从根呢去进行查找,在下方呢他有一个直接的可达对象,我们认为这是A对象, 然后紧接着他左右两侧呢有一个从跟下无法直接查找的一个区域,例如左侧我们称之为B,右侧呢我们称之为C。

那这种情况下在进行第二轮清除操作的时候,他就会直接将我们当前的这样一个B和C所对应的空间呢,进行回收。

function fn() {
    const B = '两个';
}
fn();

const A = '四个文字';

function fn2() {
    const C = '一个';
}
fn2();

然后再把这样一个释放的空间呢,去添加到我们的空闲列表之上,然后紧接着我们后续的程序呢就可以直接进来再从空闲列表上呢去申请相应的一个空间地址,进行使用。

不过,在这种情况下呢就会有一个问题,例如说,在这呢我们举例说明一下。

比如我们当前认为,任何一个空间呢都会有两个部分组成,一个呢是存储这个空间的一些元信息的比如他的一个大小,比如他的一个地址,我们称之为头。

再或者呢还有一部分是专门用于存放数据的,我们叫做域,那么这样说以后呢,我们B,C这样一个空间呢我们认为B对象呢有2个字的空间,而C对象呢有1个字的空间。

那么在这种情况下,虽然我们对他进行了回收,加起来呢好像是释放了3个字的空间,但是由于它们中间呢被我们这样一个A对象去分割着。所以在释放完成之后他们其实还是分散的,也就是地址不连续。

这点呢很重要,地址不连续,所以在这种情况下,如果后续我们想去申请一片空间,而刚好巧了,这次我们想申请的空间地址大小呢刚好是1.5个字。

那么这种情况下,如果说我们去直接找到B所释放的空间,我们会发现呢,他是多了的,因为还多了0.5个,但是如果呢我们直接去找C所释放的空间我们又发现呢她又不够,因为他是1个。

所以这个时候呢我们就造成了一个当前标记清除算法中最大的问题,叫空间的碎片化,那这里我们去简单的解释一下。

所谓的空间碎片化,就是由于我们当前所回收的这个样一个垃圾对象在地址上他本身是,不连续的,由于这种不连续,从而造成了我们在回收之后他们分散在各个角落,那后续我们要想去使用的时候,如果刚好巧了新的生成空间刚好与他们的大小匹配,那么这时候就能直接用。一旦是多了或是少了,我们就不太适合使用了。

所以这就是我们标记清除算法中的一个缺点,那么我们称之为叫做空间碎片化。

那这块呢就是关于我们标记清除算法优点和缺点的介绍,简单的整理一下就是,第一:优点呢相对于引用计数来说,我们可以去解决循环引用不能回收的问题,那么缺点呢就是相对于我们之前的一个垃圾回收来说呢他会产生一个空间碎片化的问题,不能让我们的空间呢得到最大化的使用。

那这块呢我们就介绍完了。

标记整理算法

在这里呢,我们来介绍一下标记整理算法的实现原理,和我们之前的标记清除一样,在V8当中呢,这个算法也会被频繁的使用到,那么下面我们就来看一下,标记整理算法是如何实现的。

首先,我们会认为,标记整理算法,其实就是标记清除的一个增强操作,因为他们在第一个阶段的标记工作是完全一样的,都会去遍历所有的对象,然后将当前的可达活动对象呢进行标记,只不过呢是在清除阶段,我们的标记清除是直接呢将没有标记的垃圾对象呢做空间的回收。

但是,标记整理呢会在清除之前先去执行一个整理操作,移动对象的位置,去让他们呢能够在地址上产生连续。

那这块呢为了去理解这样一个过程,我们进行一个简单的说明。

假设我们回收之前我们有很多的活动对象和非活动对象,以及一些空闲的空间,那么当他去执行我们当前标记操作的时候,会把我们所有的活动对象呢来进行标记,那么紧接着他就会进行一个整理的操作。

那么整理的是什么呢,其实就是一个位置上的改变,他会把我们当前的活动对象呢,先进性移动,然后在地址上变成连续的一个位置。

那么紧接着他就会去将当前活动对象右侧的一个范围呢去进行整体的回收。回收完成以后我们就会得到这样的一种情况,那这种情况去相对我们之前的标记清除算法来说,他的好处就会显而易见。

因为我们现在呢在内存里边就不会大批量去出现那些分散的小空间,而回收到的空间呢都基本上是连续的。

那么在后续的使用过程中,如果我们要想去申请的时候,就可以尽可能的去最大化利用我们当前内存当中所释放出来的空间。

那这个过程也就是我们的标记整理算法,那么跟我们之前所提到的一样,他会配合着我们的标记清除,在我们的V8引擎当中呢。去配合着实现频繁的GC操作。

那这里就是标记整理算法的一个基本原理介绍。

常见GC算法总结

在这里呢,我们来看一些关于常见GC算法的内容总结,首先在之前呢我们已经是见到过这样的几种算法。

第一呢,就是引用计数,他的核心思想呢就是在内部去通过一个引用计数器呢来维护每个对象呢都存在的一个引用数值,通过这个数值呢是否为0,来判断这个对象呢是否是一个垃圾对象。从而呢去回收他的一个垃圾空间,从而呢,让垃圾回收器对当前的一个空间呢进行回收释放。

第二种呢就是标记清除,那么这种算法呢,分两个阶段呢来进行,首先他会去遍历所有的对象,然后去给当前的活动对象进行标记,然后紧接着就会去把那些没有标记的对象呢去清除掉,从而去释放当前这些垃圾对象所占用的空间。

第三种呢就是标记整理,那么他的做法和其实和我们的标记清除呢类似,只不过呢在清除上有一些前置的操作,需要先去整理一下当前的地址空间。

那么接下来呢我们再分别去看一下,这些算法所各自具有的一些优缺点。

首先我们来看一下引用计数,对于他来讲他的一个优点就是可以及时回收我们的垃圾对象,因为只要这个数值为0的情况下,他就会立即去让我们的GC呢找到这样的一片空间呢进行回收和释放。

所以也正是由于这样的一个特点的存在,那么我们引用计数的另外一个优点就是,可以最大限度的去减少程序的一个卡顿,因为只要这个空间即将被占满的时候,我们的垃圾回收器呢就会进行工作,然后呢将这个内存呢去进行释放,让我们的内存空间呢总是有一些可用的地方。

那再者呢引用计数也会有一些缺点,例如他无法去回收存在着循环引用的对象,因为这样的情况意味着当前对象空间的一个引用数字呢永远是不为0的。也就是不能触发我们当前的垃圾回收操作。

除此以外他还有一个缺点就是对资源的消耗是比较大的,因为他这块呢有一个引用计数器,然后每次呢还都要去修改当前这个引用数,而这个对象空间的引用数呢有可能是很大也有可能是很小,总之呢频繁的操作会有一些资源上的开销。

因此我们认为呢他的速度就不一定是那么快了,那这块就是关于引用计数的一个基本介绍。

那么接下来我们再去看一下标记清除的优缺点,对于标记清除来说呢,他的一个好处相对于引用计数来讲就是可以回收循环引用的对象空间,那这块呢是引用计数所做不到的。

除此以外呢我们这个标记清除还有他自己的一些缺点,比如说,他呢,由于当前算法决定了他不能够去把自己所有的这样一个空间去最大化利用。所以很容易呢就产生了这种碎片化的操作。

因此这块呢是标记清除的一个小缺点,那除此以外对于标记清除来说呢,他也不能够去立即回收垃圾对象,也就是说即使在遍历的过程中他发现了,这样一个对象是不可达的,但是呢他也要等着最后呢才去清除。而且,他去清除的时候当前的程序呢其实是停止工作的,所以这也是标记清除相对来说的一个缺点。

那么最后我们再来看一下标记整理,那么他其实跟我们的标记清除其实非常类似,所以他一个优点就是可以解决我们之前标记清除的一个空间碎片化,因为他会有一个整理地址的操作在里边然后才能去做一个清除的操作。

同样,标记整理呢和我们之前的标记清除是一样的,也不能够去立即回收我们的垃圾对象,所以相对于引用计数来说呢这块也相对是一个缺点。

那么这里呢我们就完成了常见GC算法的一个整理操作,具体描述了他们的核心原理以及呢他们各自的一个优缺点,那这块呢我们就说完了。