商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > Perl6中的垃圾收集

Perl6中的垃圾收集

  发布于2026-06-29 阅读(0)

扫一扫,手机访问

Perl 的内存管理机制,对很多开发者来说既熟悉又头疼。先说说我的一个基本判断:引用计数(Reference Counting)虽然在Perl 5里用得很顺手,但在多线程和高并发环境下,它逐渐暴露出一堆历史遗留问题。Perl 6(现在是Raku)干脆换了一套思路——用可达性分析(Reachability Analysis)配合垃圾收集(GC),彻底绕开了引用计数的那些坑。下面就把这两套方案掰开揉碎了聊聊。

参考计数

在Perl 5中,每次创建一个变量,它的引用计数至少为1。只要计数大于0,对象就活着。最简单的例子:

# $a的引用计数 = 1,因为存在词法填充区里
# 词法填充区消失时,引用计数归零

如果这个值是一个对象,Perl 5会调用它的DESTROY方法:

Foo->new(); 
# $a->DESTROY 被调用

如果不涉及外部资源(比如数据库连接),及时销毁只是一种管理内存的手段,你基本不用操心何时回收。但一旦牵扯到数据库句柄这类有限的外部资源,及时销毁就是个很好的特性——引用计数能保证资源用完后立刻释放。

然而,引用计数有几个硬伤。Perl 5核心团队花了整整数年才让引用计数正常工作。如果你写过XS代码,你就知道得时刻盯着引用计数,否则要么内存泄漏,要么对象被过早销毁。

在多线程环境下,保持引用计数的同步更难——多个线程同时操作引用,任何更新丢失都会导致内存泄漏或资源无法释放。为了解决这个问题,需要加锁或用原子操作,这两者都不便宜。

  • 不过别忘了,Perl 5的ithread更像是在解释器之间做内存分叉(共享未共享的内存),而不是C语言里的线程,所以它其实不需要对引用计数加锁。

引用计数还有一个根本性缺陷:如果两个对象互相引用,它们的计数永远大于0,永远不会被销毁——这就是循环引用。实际场景中往往更复杂,比如A→B→C→A,三者互相保活。

为了绕开这个问题,Perl 5引入了弱引用(weak reference)。它能解决循环引用,但也带来性能开销,而且你得知道在哪儿用弱引用最合适——用错了,对象可能被过早销毁。

可达性分析

Perl 6从设计之初就考虑多线程,所以早期就认定引用计数在性能和维护上都有问题。它的方案是:当内存不够时,才从内存中驱逐对象——前提是该对象可以被安全移除。

在Perl 6中,你依然可以创建DESTROY方法,就像在Perl 5里一样。但请注意:你无法确定它何时(甚至是否)被调用。简单来说,只有垃圾收集运行时才会触发销毁,比如达到某个内存阈值。这时如果对象已无法被其他对象访问,且它定义了DESTROY方法,才会在回收前调用它。

程序退出时,Perl 6不会主动做垃圾收集。LEA VEEND这类相位器(phasers)会被调用,但除了相位器中代码间接触发的GC外,不会有额外的垃圾回收。

如果你需要确保程序结束时有序释放外部资源(比如数据库句柄),可以使用相位器。例如用END相位器在退出时断开数据库连接:

my $dbh = DBIish.connect("…") or die "Couldn't connect";
END $dbh.disconnect;  # 程序退出时自动断开

注意,Perl 6中的END相位器不需要花括号{ ... }。如果不加花括号,相位器里的代码和周围代码共享同一个词法块(lexpad)。

但上面的代码有一个问题:如果程序退出时数据库连接尚未建立,或者连接失败,$dbhundef,调用.disconnect会报错。Perl 6中有一个很地道的写法来避免:

END .disconnect with $dbh;

这里的with是后缀形式,只有当变量被定义(且非undef)时才会执行后面的代码,并且会把变量主题化为$_.disconnect就是$_.disconnect的简写。

如果你想在某个作用域退出时立刻释放资源,可以用LEA VE相位器:

if my $dbh = DBIish.connect("…") -> $dbh {
    LEA VE .disconnect;  # 不需要`with`,因为这里$dbh一定存在
    # 在这里使用数据库
}
say "Could not do the stuff that needed to be done";

当代码离开if作用域时,LEA VE相位器被执行。这样一来,无论中间发生什么,数据库资源都会在作用域结束时释放。

摘要

尽管Perl 6没有Perl 5用户习惯的及时销毁,但它提供了更易用的替代方案来管理外部资源。用相位器(phasers)可以做到类似甚至更好的效果,而且完全绕开了引用计数的循环引用和多线程同步问题。

(完)

本文转载于:https://www.jb51.net/article/148316.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

产品推荐

热门关注