因为并不是本人原著(我只是个“搬运工“)另外我的说明一下这里所说的GC指泛指垃圾回收机制,而单指Java或其余某种特定语言中的GC——可能具体语言中实现的垃圾回收实现机制会有所不一样。下面是具体内容:将内存管理,尤为是内存空间的释放实现自动化,这就是GC(Garbage Collection)算法
1,垃圾:缓存
所谓垃圾(Garbage),就是须要回收的对象。做为编写程序的人,是能够作出“这个对象已经不须要了“这样的判断,可是计算机是作不到的。所以若是程序(经过某个变量等等)可能会直接或间接的引用一个对象,那么这个对象就被视为“存活“;与之相反,已经引用不到的则被视为“死亡“。将这些死亡对象找出来,而后做为垃圾进行回收,者就是GC的本质。
2,根
所谓的根(Root),就是判断对象是否被引用的起始点。至于哪里的才是根,不通的语言和编译器都有不通的规定,但基本上是将变量和运行栈空间做为根。
并发
标记清除(Mark and Sweep)是最先开发出来的GC算法(1960年)。它的原理很是简单:
首先从根开始将可能被引用的对象用递归的方式进行标记,而后将没有标记到的对象做为垃圾进行回收。函数
初始状态:性能
标记阶段:线程
清除阶段:3d
上述图片显示了标记清除算法的大体原理。
“初始状态“图中显示了随着程序的运行而分配出一些对象的状态,一个对象能够对其余的对象进行引用。
“标记阶段“图中显示了GC开始执行,从根开始能够被引用的对象上进行“标记“。大多数状况下,这种标记是经过对象内部的标志(Flag)来实现的。因而,被标记的对象咱们将它涂黑。
紧接着被标记的对象所能引用的对象也会被打上标记。重复这一步骤就能够从根开始可能被间接引用到的对象所有打上标记。到此为止的操做即被称为——标记阶段(Mark phase)。标记阶段完成时,被标记的对象就是“存活“对象,反之为“死亡“对象。
标记清除算法的处理时间,是和存活对象数与对象总数的总和相关的。
做为标记清除的变形,还有一种叫作标记压缩(Mark and Compat)的算法,它不是将被标记的对象清除,而是将他们不断压缩。
复制收集方式
标记清除算法有一个缺点,就是在分配了大量对象,而且其中只有一小部分存活的状况下,所消耗的时间会大大超过必要的值,这是应为在清除阶段还须要对大量死亡对象进行扫描。
复制收集(Copy and Collection)则试图克服这一缺点。在这种算法中,会将从根开始被引用的对象复制到另外的空间中,而后,再将复制的对象所可以引用的对象用递归的方式不断复制下去。
初始状态(1)——旧空间:对象
新空间的开辟(2)——新空间:blog
复制对象(3)递归
如上图:
(1)部分是GC开始前的内存状态,者也同时表明着对象在内存中所占用的“旧空间“。
图(2)在旧空间之外开辟“新空间“并将可能从根被引用的对象复制到新空间中。
图(3)从已经复制的对象开始再将能够被引用的对象逐个复制到新空间当中……随着复制的进行,直到复制完成——最终“死亡“对象就留在了“旧空间“当中,接着将旧空间废弃掉,这样就能够将“死亡“对象所占用的空间一口气释放出来,而没有必要再次扫描“死亡“对象的必要。而等到下次GC操做是,此次所建立的“新空间“就成为了未来的“旧空间“了。
复制收集方式的过程至关于只存在于标记清除方式中的标记阶段。因为清除阶段中须要对全部对象进行扫描,这样若是在存在大量对象,且其中大量对象已经为“死亡“对象的状况下必然会形成没必要要的资源和性能上的开销。
而在复制收集方式中就不存在这样的开销。可是和标记相比,将对象复制一份的开销相对要大,所以在“存活“对象相对比例较高的状况下,反而不利。
复制收集方式的另外一个优势是:它具备局部性(Locality)。在复制收集过程当中,会按照对象被引用的顺序将对象复制到新空间中。因而,关系较近的对象被放置在距离较近的内存空间中的可能性会提升,这样被称为局部性。局部性高的状况下,内存缓存会更容易有效运做,程序的运行也可以获得提升。
引用计数方式
引用计数方式是GC算法中最简单也最容易实现的一种,它和标记清除方式差很少是同一时间被发明出来的。
它的原理是:在每一个对象中保存该对象的引用计数,当引用发生增减时对计数进行更新。
引用计数的增减,通常发生在变量复制,对象内容更新,函数结束(局部变量不在被引用),等时间点。当一个对象的引用计数为0时,则说明它未来不会再被引用,所以能够释放相应的内存空间。
1)
2)
3)
如上图:
(1)中全部对象都保存着本身被多少个对象进行引用的数量(引用计数)——图中右上角的的数字。
(2)当对象引用发生变化时,引用计数也会更者变化。在这里图中的对象B到D的引用实效后,对象D的引用计数变为0,因为对象D的引用计数变为0,所以D到E和C的引用计数也分=别减小。结果E的引用计数也变为0,因而想象E也会被释放。
(3)引用计数为0的对象被释放——“存活”对象被保留下来。而这个GC过程当中不须要对全部对象进行扫描。
优势
相比标记清除和复制收集方式实现更容易。
当对象再也不被引用的瞬间就会被释放。
其余GC机制中,要预测一个对象什么时候会被释放是很困难的,而在引用计数方式中则是当即被释放。
因为释放操做是针对个别执行的,所以和其余算法相比,由GC而产生的中断时间就比较短。
缺点
没法释放循环引用的对象。如上图A,B,C三个对象没有被其余对象引用,而是互相之间循环引用,所以他们的计数永远不会为0,结果这些对象就永远不会被释放。必须在引用发生增减时对引用计数作出正确的增减,而若是漏掉或者更改了引用计数就会引起很难找到的内存错误。引用计数不适合并行处理。若是多个线程同时对引用计数进行增减的话,引用计数的值就可能会产生不一致的问题(结果就会致使内存错误),为了不这样的事情发生,对引用计数的操做必须采用独占的方式来进行。若是引用计数操做频繁发生,每次使用都要使用加锁等并发操做其开销也不可小觑。