我试图理解标记和扫描算法如何在一段代码中实际工作。
据我所知每次使用malloc()时,我的malloc()函数都会将内存地址添加到链表中?然后,当我想要垃圾收集时,我调用mark()函数,该函数接收“root”对象,然后从那里标记所有可到达的内存地址。然后我调用sweep()并释放所有未标记的内存地址。
我对“根”对象是什么以及标记函数如何决定从“根”对象可以访问哪些对象感到困惑。
我认为,如果有人能够给我一个关于标记和扫描算法如何在一段代码中工作的小例子,那真的会对我有所帮助......或者可能会指出我可能因为某些参考我找不到任何东西。
谢谢。
答案 0 :(得分:4)
通常是根集,而不仅仅是单个根对象。根集基本上是所有全局变量,以及所有当前活动的局部变量(例如,当前在堆栈上的所有本地变量)。向C或C ++添加垃圾收集的一个真正困难是无法检测根集中指针的可能性(例如,如果您将指针写入临时文件,垃圾收集器不会知道寻找它。)
确定从那里可以达到什么意味着检查指针的任何一个,以及它指向的内容被认为是可达的。如果它指向的内容包含一个或多个指针,它会以递归方式跟随这些指针,因此可以从中获取的所有内容也被认为是可达的。
使用"精确"垃圾收集器,你有一些类型的信息可以告诉垃圾收集器什么是指针。例如,每个对象可能包含一个类型标记,以告知它是什么类型的对象。然后你有一个表告诉GC在哪里(如果有的话)那个类型的对象的哪些部分是指针。
"保守"垃圾收集器,你只需查看数据的内容,并假设如果某个可以是一个指针,它就是。你关心的唯一指针是那些进入垃圾收集堆的指针。为了便于讨论,我们假设一个64位指针和一个8兆字节的GC堆。这意味着我们查看内存并找到任何可能是指向GC堆的指针的~8百万个值中的任何值,并假设任何此类值是指针,所以无论如何在堆中的那个地址被认为是可达的(如果是这样的话,我们会在其中寻找可能是指针的任何东西,递归地)。
虽然最初听起来(对许多人来说),后者通常会将所有视为可达,但它实际上在实际使用中效果出奇。但是,它确实禁止某些垃圾收集策略。特别是,涉及压缩堆的任何事情都意味着在移动对象时需要调整指向对象的所有指针 - 为此,我们 以确定我们所做的事情。修改实际上是指向该对象的指针,而不仅仅是某个整数(或字符串等)恰好具有看起来像是指针的值。