垃圾回收

一直以为C/C++没有垃圾回收,GC都是Java那些语言的事情,最近复习一些基础知识,才发现自己还是才疏学浅啊。所以这里开个坑,记录总结一些有关垃圾回收的内容

垃圾收集

内存泄漏

在C语言或C++中,我们常用malloc或new 来分配一段内存,当我们初次学习这些语言的时候,总是被警告:malloc和free(或new delete)一定要成对出现,申请的内存不用的时候一定要释放掉,否则会导致内存泄漏。

虽然当时也并不知道什么叫内存泄漏,但这警告总是在各个版本的书上重复出现,令人印象深刻,然而在实际代码中,由于malloc语句出现的位置和需要free的位置太远,或根本不在同一层调用上,我们往往会忘记释放这些内存。

内存泄漏在某些程序上可能无关痛痒,比如一次性运行马上就要结束的程序,或在程序的生命周期内不需要反复的代码段内出现的泄漏。然而在一些需要长期运行,或是内存泄漏的代码段反复执行的程序上,这个问题是致命的,会导致系统内存被大量的无效占用,最终导致系统内存耗尽而无法继续运行。

垃圾收集

在了解Java的时候,我们可能首先会意识到这个语言和C/C++有一点巨大的不同,就是他带有GC(垃圾收集)机制,我们可以自由的申请并使用内存,而不需要关心它的释放,这一切都由垃圾收集器(garbage collector)来负责。

我们知道,在程序中我们可以利用的内存主要分为堆和栈,栈存储着一些程序的局部变量和一些局部的信息,当函数返回时,对应部分的栈内存也会跟着释放。

而堆和栈不同,堆是我们可以手动分配的一段内存区域,它的分配和利用是灵活的,不需要受到栈那样的限制,我们可以在任何时候分配和利用它。所以垃圾收集主要面向的就是堆区的内存。

当我们分配一块内存,会有着某个元素“引用”着这块内存,内存之间也会存在着“引用”关系。我们把他们描述成一个有向图

如图,堆区内有些内存节点,堆区外的一些元素作为root节点,引用着堆区内部的一些内存块,这些内存块之间也有着一些引用关系,其中有一些内存节点是已分配但不可达的(蓝色),那么这些内存就是我们需要清理的垃圾,因为其已经不可达,意味着已经没有程序可以使用这些内存了。

Mark & Sweep

标记和清除算法是应对内存垃圾的一个普遍算法,这个算法可以应用在C/C++的malloc之下,为C/C++提供GC功能。所以虽然C/C++没有GC,但我们可以手动撸一个GC出来。但C++的内存管理还是比较多样的,智能指针也可以解决一部分问题,但也会出现各种各样的循环引用的问题,所以GC在某些情况下还是更加一劳永逸的。

Mark&Sweep算法其实就是标记和清除算法,本质就是维护上面提到的有向图。

标记是指,当我们从一个root节点出发,如果本节点已经标记,或本节点为空,就立即返回,否则标记本节点并对本节点指向的其他节点递归调用标记算法。

当一个标记过程完成,那么以本节点root为起点的所有内存都已经完成了标记,也就是所有还可能用到的内存,都已经打标。

标记完成后,我们运行清理算法,这个算法会扫描内存,当遇到 已分配&&未标记 的内存块,说明这个内存块已经是 “不可达” 的垃圾内存了,那么我们就当即进行free

—–更新中—–