在高併發或長時間運行的 PHP 應用(如 CLI 腳本、Queue Worker、資料匯入/匯出)中,記憶體洩漏會導致進程 OOM(Out of Memory)被系統殺掉,甚至拖垮整台伺服器。PHP 內建的 memory_get_usage() 與 memory_get_peak_usage() 是排查效能瓶頸、優化代碼的關鍵工具。
| 函數 | 回傳值 | 單位 | 說明 |
|---|---|---|---|
memory_get_usage(bool $real_usage = false) | 目前分配給腳本的記憶體 | Bytes | $real_usage=true 取系統實際分配量(含分配器開銷),false 取 emalloc() 回報量 |
memory_get_peak_usage(bool $real_usage = false) | 腳本啟動以來的記憶體峰值 | Bytes | 同樣支援 $real_usage 參數 |
getrusage() | CPU 時間、系統呼叫等資源使用統計 | 陣列 | 僅 Linux/Unix 環境可用 |
<?php
echo '起始記憶體: ' . memory_get_usage() . " bytes\n";
$tmp = str_repeat('hello', 1000); // 佔用約 5KB
echo '建立大字串後: ' . memory_get_usage() . " bytes\n";
unset($tmp); // 釋放變量
echo 'unset 後: ' . memory_get_usage() . " bytes\n";
// 進階:搭配記憶體峰值觀察
$arr = range(1, 100000);
echo '峰值記憶體: ' . memory_get_peak_usage() . " bytes\n";
輸出範例:
起始記憶體: 384,520 bytes
建立大字串後: 389,680 bytes
unset 後: 384,528 bytes
峰值記憶體: 412,344 bytes
關鍵觀察:unset()能立即釋放變量佔用的記憶體回到基準線;memory_get_peak_usage()記錄的是歷史最高點,適合在腳本結束前輸出,掌握整體記憶體上限。
function formatBytes(int $bytes, int $precision = 2): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
// 使用範例
echo '目前記憶體: ' . formatBytes(memory_get_usage(true)) . "\n";
echo '峰值記憶體: ' . formatBytes(memory_get_peak_usage(true)) . "\n";
// ❌ 錯誤:一次載入 100 萬筆資料到陣列
$all = $pdo->query('SELECT * FROM big_table')->fetchAll();
// ✅ 正確:使用 Generator 分批處理
function fetchBatch(PDO $pdo, int $batch = 1000): Generator
{
$stmt = $pdo->query('SELECT * FROM big_table');
$batchData = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$batchData[] = $row;
if (count($batchData) >= $batch) {
yield $batchData;
$batchData = []; // 釋放上一批記憶體
}
}
if ($batchData) yield $batchData;
}
foreach (fetchBatch($pdo) as $batch) {
process($batch); // 處理完即釋放
}
$stmt = $pdo->query('SELECT * FROM huge_table');
while ($row = $stmt->fetch()) { /* ... */ }
$stmt = null; // 明確釋放 PDOStatement 佔用的記憶體
// 或 mysqli_free_result($result); // mysqli 寫法
// 在迴圈關鍵點插入監控
if (memory_get_usage(true) > 200 * 1024 * 1024) { // 超過 200MB
error_log('Memory limit approaching: ' . formatBytes(memory_get_usage(true)));
// 可選:重啟 Worker、暫停處理、發送告警
}
getrusage() 不支援;memory_get_usage() 在所有平台均可用。memory_get_usage(true) 反映的是向 OS 申請的總量,通常大於 false 回傳值。unset() 後記憶體不一定立即歸還給 OS。善用 memory_get_usage() 與 memory_get_peak_usage(),配合分批處理、及時 unset()、Generator 模式,能讓 PHP 應用在有限記憶體下穩定處理大量資料。建議將記憶體監控納入 CI/CD 流程,設定閾值自動報警,從源頭杜記憶體溢位風險。