2010-01-28 21 views
2

我有一個在Apache上運行的傳統Perl CGI頁面,它處理大量Excel電子表格中的數據,並根據需要添加到數據庫中。它按照數據組進行處理,並將每組數據發送到數據庫。如何在Perl CGI腳本中調試可能的內存泄漏?

在每次調用數據庫之後,我的系統可用內存顯着減少,直到沒有剩餘內存。一旦我終於得到'腳本頭部提前結束'錯誤並且HTTP代碼500被返回給客戶端,內存就被釋放回系統。

翻閱(複雜的)代碼,我找不到內存泄漏可能發生的地方。是否有一些技巧或工具可以用來確定內存的位置?

+0

什麼版本的Perl和DBI?每當我們嘗試在一個點上創建一個新的準備好的語句時,就會陷入內存泄漏,但它不是那麼糟糕。 – fennec 2010-01-29 00:04:51

+0

在一次調用CGI腳本時內存是否會爆炸,或者只是隨着重複的calll而增加,直到它最終失敗? – 2010-01-29 01:17:54

回答

2

簡短的回答是,它很糟糕。沒有一個很好的,隨時可用的程序,您可以運行以獲得答案。對不起,我沒有更多的幫助,但沒有看到任何代碼等,真的沒有任何人可以提供更好的建議。

我不能談論你的特定情況,但這裏有一些我過去做過的事情。找出造成問題的一般區域非常重要。這與其他調試技術沒有多大區別。通常我發現這些東西沒有優雅的解決方案。你只要捲起你的袖子,不管它的氣味有多糟,你的手臂都會肘部深陷在泥土中。

首先,在Web服務器之外運行程序。如果您仍然從命令行看到問題,請高興:您只是(大部分)排除了Web服務器的問題。這可能需要一些工作來創建一個包裝腳本來設置Web環境,但最終會變得更容易,因爲您不需要重新啓動服務器等來重置環境。

如果你不能在服務器外複製問題,你仍然可以做我接下來推薦的東西,它只是更煩人。如果這是一個web服務器問題,而不是命令行中的問題,那麼這項任務就成爲了解這兩者之間差異的發現。我遇到過這樣的情況。

如果它不是Web服務器的問題,請開始平分腳本,就像您對任何調試問題一樣。如果您啓用了日誌記錄,請將其打開並在記錄實際內存使用情況時觀察程序運行。它什麼時候炸燬?這聽起來像你已經縮小到一些數據庫調用。如果你能夠通過命令行或者調試器運行它,我會在內存增加之前和之後找到一對合適的斷點,並逐漸將它們放在一起。您可以使用諸如Devel::Size之類的模塊來查看您懷疑的數據結構的內存大小。

從那裏它只是縮小犯罪嫌疑人。一旦找到嫌疑人,看看你是否可以用簡短的示例腳本複製它。你想盡可能地消除促成因素的可能性。

一旦您認爲自己已經找到了違規代碼,也許您可​​以提出另一個問題,如果您仍然不明白正在發生的事情,則可以顯示代碼。

如果你想變得很花哨,你可以編寫你自己的Perl調試器。這並不難。您有機會在語句開頭或結尾處的DB名稱空間中運行一些子例程。你有你懷疑的東西的調試代碼列表內存配置文件,並尋找內存大小的跳轉。除非一切都失敗,否則我不會嘗試。

+0

這幾乎是我在過去做過所有的Perl調試。我真的希望有一些非常有用的工具,我從來沒有聽說過。感謝您的深入策略。 – Aaron 2010-01-29 13:05:09

-1

如果問題出在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; 
    } 
} 

如果可能的話,我會建議將子對象的引用從子對象中刪除。

+0

這不是確定內存泄漏的方法。這是一個perl代碼如何泄漏內存的例子,雖然Op沒有說我認爲插件是用C/C++編寫的,所以在這種情況下你必須手動增加和減少ref的數量。 – rook 2010-01-28 22:29:13

-1

如何寫入數據庫?如果您使用的是任何DBI軟件包或自定義包裝,請確保您正在刷新所有可緩存的對象或緩存變量。這些類型的內存膨脹問題比較常見,並且通常代表某個持續存在的共享對象緩存。

事情嘗試:

  • 清除對象變量時,與他們完成
  • 深循環數據庫連接(這是極端的,但是這取決於你如何連接,它可以解決這個問題)。
  • 一次只加載一組數據,並刷新組之間的任何變量或工廠對象。

希望有助於一些。