与 Java 不同,Perl 使用引用计数进行垃圾收集。我曾尝试搜索一些关于 C++ RAII 和智能指针以及 Java GC 的先前问题,但不了解 Perl 如何处理循环引用问题。
谁能解释 Perl 的垃圾收集器如何处理循环引用?有没有办法回收程序不再使用的循环引用内存,或者 Perl 完全忽略了这个问题?
与 Java 不同,Perl 使用引用计数进行垃圾收集。我曾尝试搜索一些关于 C++ RAII 和智能指针以及 Java GC 的先前问题,但不了解 Perl 如何处理循环引用问题。
谁能解释 Perl 的垃圾收集器如何处理循环引用?有没有办法回收程序不再使用的循环引用内存,或者 Perl 完全忽略了这个问题?
根据我的Programming Perl 3rd ed 的副本。,在退出 Perl 5 时会执行“昂贵的标记和扫描”来回收循环引用。您将希望尽可能避免循环引用,否则在程序退出之前它们不会被回收。
Perl 5 确实通过Scalar::Utils模块提供了弱引用。
Perl 6 将转向可插入的垃圾收集方案(嗯,底层 VM 将有多个垃圾收集选项,并且这些选项的行为会对 Perl 产生影响)。也就是说,您将能够在各种垃圾收集器之间进行选择,或者实现您自己的。想要一个复制收藏家?当然。想要着色器?你说对了。标记/扫描、压缩等?为什么不?
快速的回答是 Perl 5 不会自动处理循环引用。除非您在代码中采取明确的措施,否则在创建它们的线程死亡之前,您的任何包含循环引用的数据结构都不会被回收。这被认为是一个可接受的折衷方案,因为它避免了运行时垃圾收集的需要,这会减慢执行速度。
如果您的代码创建具有循环引用的数据结构(即,其节点包含指向根节点的引用的树),您将需要使用 Scalar::Util 模块来“弱化”指向根节点的引用。这些弱引用不会增加它们所指向的引用计数,因此当最后一个外部引用消失时,整个数据结构将被自动释放。
例子:
use Scalar::Util qw(weaken);
...
my $new_node = { content => $content, root => $root_node };
weaken $new_node->{root};
push @{$root_node->{children}}, $new_node;
如果每次向数据结构中添加新节点时都使用这样的代码,那么实际计算的对根的唯一引用是来自结构外部的引用。这正是你想要的。然后,一旦对它的最后一个外部引用消失,就会回收根,并递归地回收它的所有子代。
看看代理对象。
Perl 在某些情况下(我认为当线程死亡时)应用标记和清除备用 GC 以回收循环引用。请注意,“每个值都是一个字符串”Perl 节使得创建真正的循环引用变得困难;这是可行的,但“正常”的 Perl 代码不可行,这就是引用计数在 Perl 中运行良好的原因。