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

PHP 記憶體管理機制詳解:ZendMM、持久/請求記憶體與洩漏防護

     編輯:PHP 綜合知識

PHP 最大特色是開發者無需手動管理記憶體指標。本文深入剖析 Zend Memory Manager (ZendMM) 如何在請求級別自動回收記憶體、區分持久/非持久分配、防護 longjmp() 導致的洩漏。

C 與 PHP 記憶體管理差異

傳統請求/釋放模型痛點

  • 伺服器行程長期運行,微小洩漏累積導致 OOM。
  • 錯誤處理(die()、E_ERROR)觸發 longjmp() 跳出呼叫堆疊,**清理碼(free())被略過**,造成洩漏。

ZendMM 解決方案

ZendMM 與 OS 記憶體關係圖

  • ZendMM 介於 PHP 程式碼與 OS 之間,扮演「進程內記憶體管理者」。
  • 向 OS 申請大塊記憶體(Chunk),內部劃分為小塊分配給 emalloc()、erealloc()。
  • 請求結束時,ZendMM 一次性釋放該請求所有分配,無需逐一 efree()。
  • 解決 longjmp() 導致清理碼被跳過的洩漏問題。

兩類記憶體分配 API

場景C 語言PHP
字串指派char *str = strdup("hello"); 需手動 free()$str = 'hello'; 自動管理
修改字串需重新分配、複製、釋放舊記憶體直接 $str = 'world'; 內部 COW 處理
洩漏後果長期行程累積耗盡記憶體請求結束 ZendMM 自動全數回收

宏定義:pemalloc(size, persistent) 中 persistent=1 走持久分配,0 走請求分配。

錯誤跳躍洩漏示範與 ZendMM 保護

void call_function(const char *fname, int fname_len TSRMLS_DC) {
    char *lcase_fname = estrndup(fname, fname_len); // 請求級分配
    zend_str_tolower(lcase_fname, fname_len);
    
    if (zend_hash_find(EG(function_table), lcase_fname, fname_len+1, (void**)&fe) == FAILURE) {
        zend_execute(fe->op_array TSRMLS_CC);
    } else {
        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Call to undefined function: %s()", fname);
        // longjmp() 直接跳出,efree(lcase_fname) 被略過
    }
    efree(lcase_fname); // 正常路徑才會執行
}

若無 ZendMM,lcase_fname 記憶體永久洩漏。有 ZendMM 時,請求結束自動回收。

開發者最佳實踐

  1. 擴展開發**優先使用 emalloc()/efree()**,除非確需跨請求持久。
  2. 儘量在函式結束前 efree() 釋放大型暫存記憶體,降低請求峰值記憶體。
  3. 啟用 --enable-debug 編譯 PHP,請求結束會自動報告未釋放的請求級記憶體(洩漏偵測)。
  4. 避免在擴展中直接呼叫系統 malloc()/free(),繞過 ZendMM 保護。

記憶體洩漏偵測範例

void show_value(int n) {
    char *message = emalloc(1024);
    sprintf(message, "The value of n is %d\n", n);
    php_printf("%s", message);
    // 忘記 efree(message)
}

開啟 --enable-debug 後,請求結束輸出:

/cvs/php5/ext/sample/sample.c(33) : Freeing 0x084504B8 (1024 bytes), script=-
=== Total 1 memory leaks detected ===

精確定位洩漏分配位置(檔案、行號、大小),極大提升除錯效率。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
API用途釋放時機範例
emalloc()/efree()請求級記憶體請求結束 (RSHUTDOWN) 自動釋放變數值、暫存陣列、模板編譯結果
pemalloc()/pefree()持久記憶體SAPI 關閉 (MSHUTDOWN) 釋放資源類型結構、常數表、類別定義、連線池