我有一個在Apache上運行的傳統Perl CGI頁面,它處理大量Excel電子表格中的數據,並根據需要添加到數據庫中。它按照數據組進行處理,並將每組數據發送到數據庫。如何在Perl CGI腳本中調試可能的內存泄漏?
在每次調用數據庫之後,我的系統可用內存顯着減少,直到沒有剩餘內存。一旦我終於得到'腳本頭部提前結束'錯誤並且HTTP代碼500被返回給客戶端,內存就被釋放回系統。
翻閱(複雜的)代碼,我找不到內存泄漏可能發生的地方。是否有一些技巧或工具可以用來確定內存的位置?
我有一個在Apache上運行的傳統Perl CGI頁面,它處理大量Excel電子表格中的數據,並根據需要添加到數據庫中。它按照數據組進行處理,並將每組數據發送到數據庫。如何在Perl CGI腳本中調試可能的內存泄漏?
在每次調用數據庫之後,我的系統可用內存顯着減少,直到沒有剩餘內存。一旦我終於得到'腳本頭部提前結束'錯誤並且HTTP代碼500被返回給客戶端,內存就被釋放回系統。
翻閱(複雜的)代碼,我找不到內存泄漏可能發生的地方。是否有一些技巧或工具可以用來確定內存的位置?
簡短的回答是,它很糟糕。沒有一個很好的,隨時可用的程序,您可以運行以獲得答案。對不起,我沒有更多的幫助,但沒有看到任何代碼等,真的沒有任何人可以提供更好的建議。
我不能談論你的特定情況,但這裏有一些我過去做過的事情。找出造成問題的一般區域非常重要。這與其他調試技術沒有多大區別。通常我發現這些東西沒有優雅的解決方案。你只要捲起你的袖子,不管它的氣味有多糟,你的手臂都會肘部深陷在泥土中。
首先,在Web服務器之外運行程序。如果您仍然從命令行看到問題,請高興:您只是(大部分)排除了Web服務器的問題。這可能需要一些工作來創建一個包裝腳本來設置Web環境,但最終會變得更容易,因爲您不需要重新啓動服務器等來重置環境。
如果你不能在服務器外複製問題,你仍然可以做我接下來推薦的東西,它只是更煩人。如果這是一個web服務器問題,而不是命令行中的問題,那麼這項任務就成爲了解這兩者之間差異的發現。我遇到過這樣的情況。
如果它不是Web服務器的問題,請開始平分腳本,就像您對任何調試問題一樣。如果您啓用了日誌記錄,請將其打開並在記錄實際內存使用情況時觀察程序運行。它什麼時候炸燬?這聽起來像你已經縮小到一些數據庫調用。如果你能夠通過命令行或者調試器運行它,我會在內存增加之前和之後找到一對合適的斷點,並逐漸將它們放在一起。您可以使用諸如Devel::Size之類的模塊來查看您懷疑的數據結構的內存大小。
從那裏它只是縮小犯罪嫌疑人。一旦找到嫌疑人,看看你是否可以用簡短的示例腳本複製它。你想盡可能地消除促成因素的可能性。
一旦您認爲自己已經找到了違規代碼,也許您可以提出另一個問題,如果您仍然不明白正在發生的事情,則可以顯示代碼。
如果你想變得很花哨,你可以編寫你自己的Perl調試器。這並不難。您有機會在語句開頭或結尾處的DB
名稱空間中運行一些子例程。你有你懷疑的東西的調試代碼列表內存配置文件,並尋找內存大小的跳轉。除非一切都失敗,否則我不會嘗試。
這幾乎是我在過去做過所有的Perl調試。我真的希望有一些非常有用的工具,我從來沒有聽說過。感謝您的深入策略。 – Aaron 2010-01-29 13:05:09
如果問題出在Perl代碼中,那麼您可能有一個指向自己的引用或父節點。
下面是展示此行爲的代碼的快速示例。
{
my @a;
@a = [\@a];
}
通常它以對象的形式引用父對象。
{ 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;
}
}
如果可能的話,我會建議將子對象的引用從子對象中刪除。
這不是確定內存泄漏的方法。這是一個perl代碼如何泄漏內存的例子,雖然Op沒有說我認爲插件是用C/C++編寫的,所以在這種情況下你必須手動增加和減少ref的數量。 – rook 2010-01-28 22:29:13
如何寫入數據庫?如果您使用的是任何DBI軟件包或自定義包裝,請確保您正在刷新所有可緩存的對象或緩存變量。這些類型的內存膨脹問題比較常見,並且通常代表某個持續存在的共享對象緩存。
事情嘗試:
希望有助於一些。
什麼版本的Perl和DBI?每當我們嘗試在一個點上創建一個新的準備好的語句時,就會陷入內存泄漏,但它不是那麼糟糕。 – fennec 2010-01-29 00:04:51
在一次調用CGI腳本時內存是否會爆炸,或者只是隨着重複的calll而增加,直到它最終失敗? – 2010-01-29 01:17:54