首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 記憶體監控實戰:memory_get_usage() 與 peak_usage 完整解析

     編輯:PHP 相關知識

前言:為何要監控記憶體?

在高併發或長時間運行的 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() 記錄的是歷史最高點,適合在腳本結束前輸出,掌握整體記憶體上限。

實用工具函數:將 Bytes 轉為人類可讀單位

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";

效能優化實戰技巧

1. 大量資料處理時分批釋放

// ❌ 錯誤:一次載入 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); // 處理完即釋放
}

2. 釋放資料庫結果集

$stmt = $pdo->query('SELECT * FROM huge_table');
while ($row = $stmt->fetch()) { /* ... */ }
$stmt = null; // 明確釋放 PDOStatement 佔用的記憶體
// 或 mysqli_free_result($result); // mysqli 寫法

3. 監控長時間運行腳本

// 在迴圈關鍵點插入監控
if (memory_get_usage(true) > 200 * 1024 * 1024) { // 超過 200MB
    error_log('Memory limit approaching: ' . formatBytes(memory_get_usage(true)));
    // 可選:重啟 Worker、暫停處理、發送告警
}

常見誤區與注意事項

  • 僅限 CLI / Linux 環境: Windows 下 getrusage() 不支援;memory_get_usage() 在所有平台均可用。
  • 不等於系統進程記憶體: PHP 內部分配器(Zend Memory Manager)會向 OS 申請大塊記憶體再切分,memory_get_usage(true) 反映的是向 OS 申請的總量,通常大於 false 回傳值。
  • opcache 影響: 開啟 OPcache 時,編譯後的 opcode 共享記憶體不計入此函數統計。
  • 垃圾回收機制: PHP 5.3+ 引入 GC,循環引用的物件需等待 GC 回收,unset() 後記憶體不一定立即歸還給 OS。

結語

善用 memory_get_usage() 與 memory_get_peak_usage(),配合分批處理、及時 unset()、Generator 模式,能讓 PHP 應用在有限記憶體下穩定處理大量資料。建議將記憶體監控納入 CI/CD 流程,設定閾值自動報警,從源頭杜記憶體溢位風險。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved