[英]Common Perl memory/reference leak patterns?
我在Perl代碼庫中追逐幾個潛在的內存泄漏,我想知道Perl中有關內存(錯誤)管理的常見缺陷。
您在Perl代碼中觀察到的常見泄漏模式是什么?
到目前為止,
循環引用是泄漏的典型原因。
sub leak {
my ($foo, $bar);
$foo = \$bar;
$bar = \$foo;
}
Perl使用引用計數垃圾收集。 這意味着perl會保留在給定時間存在指向任何變量的指針的計數。 如果變量超出范圍且計數為0,則清除變量。
在上面的示例代碼中,永遠不會收集$foo
和$bar
,並且每次調用leak()
后都會保留一個副本,因為兩個變量的引用計數都是1。
防止此問題的最簡單方法是使用弱引用。 弱引用是您訪問數據時所遵循的引用,但不計入垃圾回收。
use Scalar::Util qw(weaken);
sub dont_leak {
my ($foo, $bar);
$foo = \$bar;
$bar = \$foo;
weaken $bar;
}
在dont_leak()
, $foo
的引用計數為0, $bar
的引用計數為1.當我們離開子例程的范圍時, $foo
返回到池中,並且它對$bar
引用被清除。 這會將$bar
上的引用計數降為0,這意味着$bar
也可以返回池中。
更新:腦子問我是否有任何數據來支持循環引用很常見的斷言。 不,我沒有任何統計數據顯示循環引用很常見。 它們是perl內存泄漏最常被談論和最佳記錄形式。
我的經驗是他們確實發生了。 這是我在使用Perl十多年后看到的內存泄漏的快速概述。
我遇到了pTk應用程序開發泄漏的問題。 我能夠證明的一些泄漏是由於當Tk通過窗口參考時出現的循環引用。 我也看到了pTk泄漏,其原因我永遠無法追查。
我看到人們的誤解weaken
了,偶然發現了循環引用。
當太多經過深思熟慮的物體被匆忙拋到一起時,我已經看到無意的循環。
有一次,我發現來自XS模塊的內存泄漏正在創建大而深的數據結構。 我從來沒有能夠獲得比整個程序更小的可重現的測試用例。 但是當我用另一個串行器替換模塊時,泄漏就消失了。 所以我知道這些漏洞來自XS。
因此,根據我的經驗,周期是泄漏的主要來源。
幸運的是, 有一個模塊可以幫助追蹤它們。
至於從未得到清理的大型全球結構是否構成“泄密”,我同意布萊恩的意見。 他們像泄漏一樣嘎嘎叫(由於一個bug,我們的進程內存使用量不斷增長),所以它們是泄漏的。 即便如此,我也記得在野外看不到這個特殊的問題。
根據我在巨石陣的網站上看到的內容,我猜布萊恩看到了很多來自他正在訓練的人或者為他們制作治療奇跡的病假代碼。 所以他的樣本集比我的樣本集更容易變化,但它有自己的選擇偏差。
哪種泄漏原因最常見? 我認為我們真的不知道。 但是我們都同意循環引用和全局數據垃圾是反模式,需要在可能的情況下消除,並在有意義的少數情況下謹慎處理。
如果問題出在Perl代碼中,則可能有一個指向自身的引用或父節點。
通常它以對象的形式出現,引用父對象。
{ package parent;
sub new{ bless { 'name' => $_[1] }, $_[0] }
sub add_child{
my($self,$child_name) = @_;
my $child = child->new($child_name,$self);
$self->{$child_name} = $child; # saves a reference to the child
return $child;
}
}
{ package child;
sub new{
my($class,$name,$parent) = @_;
my $self = bless {
'name' => $name,
'parent' => $parent # saves a reference to the parent
}, $class;
return $self;
}
}
{
my $parent = parent->new('Dad');
my $child = parent->add_child('Son');
# At this point both of these are true
# $parent->{Son}{parent} == $parent
# $child->{parent}{Son} == $child
# Both of the objects **would** be destroyed upon leaving
# the current scope, except that the object is self-referential
}
# Both objects still exist here, but there is no way to access either of them.
解決這個問題的最好方法是使用Scalar :: Util :: weaken 。
use Scalar::Util qw'weaken';
{ package child;
sub new{
my($class,$name,$parent) = @_;
my $self = bless {
'name' => $name,
'parent' => $parent
}, $class;
weaken ${$self->{parent}};
return $self;
}
}
如果可能的話,我建議從子進程中刪除對父對象的引用。
我以前遇到過XS的問題,包括我自己的手工卷制和CPAN模塊,如果管理不當,內存會從C代碼中泄露出來。 我從未設法追蹤泄漏; 該項目處於緊迫的最后期限並且具有固定的運行壽命,因此我通過每日cron
重啟來解決問題。 cron
真是太棒了。
CPAN的一些模塊使用循環引用來完成它們的工作,例如HTML :: TreeBuilder (代表HTML樹)。 它們將要求你在最后運行一些破壞方法/例程。 剛看完文檔:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.