Php

如何確定基於 Apache/PHP 的 Web 應用程序中明顯記憶體洩漏的原因?

  • May 7, 2013

大約每週一次,但有時在執行好幾天后甚至一天幾次,我的 EC2 實例變得無響應。Munin 的記憶體圖講述了一個非常簡單的故事:分配給“應用程序”的記憶體開始增長,並且在交換完全使用並且實例有效地癱瘓之前不會停止。另一個自定義圖顯示,不斷增長的程序是apache2。

我使用 mod_php 和一些 PHP 腳本執行標準的 prefork Apache 設置。正如您在下圖中看到的那樣,發生了一些事情,觸發 apache2 程序開始消耗越來越多的記憶體。我及時發現了第一個綠色尖峰,並在事情失控之前重新啟動了 Apache。第二個尖峰變得更遠了,必須立即重新啟動實例。

穆寧記憶圖

我想知道的是如何最好地調試它。如果沒有使用 FastCGI 設置 PHP 並讓它在自己的程序中執行,那麼有什麼好方法可以確定是 Apache 還是 PHP 和我的程式碼的組合導致了過多的記憶體使用?你們會採取什麼步驟來追踪這個問題?


更新:正如馬特在下面建議的那樣,在涉及 strace 後,我能夠追踪洩漏。

在找到一個在記憶體中逐漸且持續增長的 apache2 程序後,我在我的 PHP 腳本中添加了幾個 error_log() 呼叫,以列印出在其執行過程中不同點使用的 RSS 總量(使用 ps 的輸出)。然而,事實證明這具有誤導性——雖然似乎 RSS 只是在我的腳本執行完成後才跳轉,但後來的調試顯示情況並非如此。當心!

幸運的是,所有這些 error_log() 呼叫最終都證明是有用的。當我啟動 strace ( strace -p <pid> -tt -o trace.log -s 256) 時,我看到對於每個請求,該程序分配了大約 400k 的記憶體(查找 ‘brk’ 系統呼叫並從最後一個呼叫中減去第一個呼叫的參數——通常會有幾個一個接一個)。然後,我搜尋了包含我的 error_log() 消息的最新“寫入”系統呼叫,該消息告訴我在腳本中的哪個位置分配了記憶體。通過一些更具戰略性的 error_log() 呼叫來更準確地確定位置,我終於找到了罪魁禍首。

當我們從 PHP 腳本呼叫 curl_exec() 時,記憶體正在洩漏。一些與處理 SSL 連接相關的 curl 程式碼做錯了——當我切換到 HTTP 時洩漏就消失了。Curl 的變更日誌引用了一些在 7.19.5 中修復的 SSL 記憶體洩漏(我們在 7.18.2 上),所以我接下來會嘗試。

與此同時,我執行的 MaxRequestsPerChild 非常低,這使 Apache 保持在合理的範圍內。感謝大家!

追查導致問題的原因可能會讓人頭疼。如果我遇到這樣的問題,我要做的第一件事就是減少MaxRequestsPerChild到一個非常低的數字(~100-200),看看這是否會有所作為。如果是這樣,那麼您可能有程式碼在某處循環洩漏記憶體,您需要執行程式碼審核。

另一件要查看的是 Apache 的 fullstatus,看看您是否可以找出導致記憶體洩漏的特定請求。獲取可疑程序的 PID 並對其執行 strace。

引用自:https://serverfault.com/questions/88997