perl5和perl6
在本系列的第一篇有关将Perl 5代码迁移到Perl 6的文章中,我们研究了在移植代码时可能遇到的一些问题。 在第二篇文章中,我们将介绍Perl 6中垃圾回收的不同之处。
在Perl 6中没有及时销毁对象。这种启示通常会给习惯于Perl 5中销毁对象语义的人们带来很大的震撼。但是不用担心,Perl 6中还有其他方法可以实现相同的行为,尽管需要开发者多加思考。 首先让我们研究一下Perl 5中的情况。
参考计数
在Perl 5中,可以通过引用计数来及时销毁“超出范围”的对象。 在Perl 5中创建内容时,它的引用计数为1或更大,从而使它保持活动状态。 在最简单的情况下,它看起来像这样:
{
my
$a
=
42
;
# reference count of $a = 1, because lives in lexical pad
}
# lexical pad is gone, reference count to 0
在Perl 5中,如果该值是一个对象(又名有福),则将在其上调用DESTROY
方法。
{
my
$a
= Foo
->
new
;
}
# $a->DESTROY called
如果不涉及外部资源,及时销毁只是管理程序使用的内存的另一种方法。 而且,作为程序员,您无需关心如何以及何时回收这些东西。 话虽如此,如果您需要处理外部资源(例如数据库句柄)(数据库服务器通常只提供有限的数量),及时销毁是一个非常不错的功能。 引用计数可以提供这一点。
但是,引用计数有几个缺点。 Perl 5核心开发人员花了很多年才能使引用计数正常工作。 而且,如果您在XS中工作,则始终需要注意引用计数,以防止内存泄漏或过早破坏。
在多线程环境中,保持事物同步变得更加困难,因为您不想丢失对同时来自多个线程的引用的任何更新(因为这将导致内存泄漏和/或外部资源无法释放) 。 为了避免这种情况,将需要某种锁定或原子更新,但都不便宜。
请注意,与诸如C之类的编程语言中的线程相比,Perl 5 ithreads更像是在解释器之间具有未共享内存的内存中派生。因此,它的引用计数仍然不需要任何锁定。
引用计数还具有一个基本的缺点,即如果两个对象包含彼此的引用,则它们将永远不会被破坏,因为它们会将彼此的引用计数保持在0(循环引用)以上。 在实践中,这通常会更深入,更像是A -> B -> C -> A
,其中A,B和C彼此保持生命。
弱引用的概念是为了在Perl 5中规避这些情况而开发的。尽管这可以解决循环引用问题,但是它具有性能影响,并且不能解决首先具有(并找到)循环引用的问题。 你必须能够找出一个弱引用可以以最佳的方式使用; 否则,可能会导致不必要的对象过早破坏。
可达性分析
由于Perl 6的内核是多线程的,因此在很早的时候就决定引用计数在性能和维护方面都是有问题的。 相反,当需要更多内存并且可以安全删除对象时,会将对象从内存中逐出。
可以像在Perl 5中一样创建DESTROY
方法。但是您无法确定何时(如果有)将调用该方法。
无需过多讨论 ,Perl 6中的对象仅在启动垃圾回收运行时(例如,达到某个内存限制时)才被销毁。 只有这样,如果对象不能再通过在存储器中的其它物体接触到它有一个DESTROY
方法,将它称为只是之前被取出物体。
程序退出时,Perl 6不会进行垃圾回收。 适用的相位器 (例如LEAVE
和END
) 将被调用,但是除了(间接地)由相位器中运行的代码启动的垃圾回收之外,不会执行任何垃圾回收。
如果始终需要有序地关闭程序所使用的外部资源(例如数据库句柄),则可以使用移相器来确保以适当,及时的方式释放外部资源。
例如,您可以使用END
相位器(在Perl 5中称为END
块)在程序退出时(无论出于何种原因)与数据库正确断开连接:
请注意, END
相位器在Perl 6中不需要块(如{ ... }
)。如果没有,则相位器中的代码与周围的代码共享词汇垫(lexpad)。
上面的代码中有一个缺陷:如果程序在建立数据库连接之前退出,或者由于某种原因数据库连接失败,它将仍然尝试对$dbh
任何内容调用.disconnect
方法,这将导致执行错误。 然而, 有一个简单的成语,以规避在Perl 6的这种情况下使用 。
END . disconnect with $dbh ;
只有定义了给定值(通常是实例化的对象),然后将其主题化为$_
, with
的后缀才匹配。 .disconnect
是$_.disconnect
.disconnect
的缩写。
如果您希望在退出特定范围时清理外部资源,则可以在该范围内使用LEAVE
移相器。
if DBIish
.
connect
(
...
)
->
$dbh
{
LEAVE
$dbh
. disconnect
;
# no need for `with` here
# do your stuff with the database
}
else
{
say
"Could not do the stuff that needed to be done"
;
}
只要剩下if
的范围,任何LEAVE
移相器都会执行。 因此,只要代码在该范围内运行,数据库资源就会被释放。
摘要
尽管Perl 6没有像Perl 5用户那样及时销毁对象,但它确实具有易于使用的替代方法来确保外部资源的管理,这与Perl 5中的类似。
翻译自: https://opensource.com/article/18/8/garbage-collection-perl-6
perl5和perl6