Perl使用引用计数算法进行自动内存管理:每个变量都有一个引用计数。当计数变为零时,内存被释放。在大多数情况下,这个过程是透明的——未使用的变量在出作用域时会被删除。
问题出现在循环引用时(例如,一个对象自我引用或者两个结构相互引用)。在这种情况下,引用计数永远不会变为零,内存将不会被释放。
为防止泄漏,可以使用模块 Scalar::Util::weaken —— 它允许创建“弱引用”,不会增加引用计数。
示例:
use Scalar::Util qw(weaken); my $a = {}; my $b = { ref => $a }; $a->{ref} = $b; weaken($a->{ref}); # 现在没有循环强依赖
是否可以合理地认为Perl总是自动释放所有未使用的内存,即使存在复杂的相互关联结构?
答案和示例:
不!在循环引用的情况下,Perl无法自动释放内存,除非使用 weaken:
my $a = {}; $a->{self} = $a; # 循环 # $a将永远不会自动删除——需要手动断开或弱化引用
故事1:在大型Perl web服务中存在内存泄漏——用户会话在哈希中保存了相互之间的引用,并且没有人使用弱引用。服务在一天内耗尽了所有资源,崩溃并需要重新启动。
故事2:自定义ORM在User和Group对象之间创建了循环,每个对象相互引用。在出作用域后,对象仍然保留在内存中——服务逐渐“膨胀”到数十GB!
故事3:将匿名子程序(“闭包”)作为类的方法引用
$self,在每个对象创建时导致泄漏,直到出现分析器,识别出循环引用并指出需要使用weaken。