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

PHP 數組記憶體佔用深度分析:從 zval 到 HashTable 的底層結構解析

     編輯:PHP 綜合知識

PHP 數組底層是有序映射(HashTable),其記憶體開銷遠超 C 語言原生數組。理解內存模型,有助於在大數據場景下做出正確的數據結構選型。本文通過實測、源碼結構拆解、多維數組對比,量化分析 PHP 數組的真實佔用。

一、實測基準:10k 元素整數數組

<?php
echo "init mem: " . memory_get_usage() . "\n";      // 58,468 B
$a = array_fill(0, 10000, 1);
echo "10k elements: " . memory_get_usage() . "\n";  // 724,696 B ≈ 66 B/元素
$b = array_fill(0, 10000, 1);
echo "20k elements: " . memory_get_usage() . "\n";  // 1,390,464 B ≈ 66 B/元素

單純整數元素平均佔用 **~66 字節**,而 C 語言 int[10000] 僅需 40 KB(4 字節/元素),PHP 開銷約 **16 倍**。

二、底層結構拆解(PHP 7/8 核心)

1. zval:統一值容器(16 字節,64 位)

struct _zval_struct {
    zend_value        value;   // 8 字節 union
    zend_refcounted  *gc;      // 8 字節指針(引用計數/GC)
    union { ... } u1;          // 類型標誌等
    union { ... } u2;          // 保留
};

每個變量、數組元素、對象屬性本質都是一個 zval。

2. HashTable:數組核心結構(~56 字節固定開銷)

struct _zend_array {  // 即 HashTable
    zend_refcounted_h gc;
    union { ... } u;
    uint32_t          nTableMask;
    Bucket           *arData;      // 指向連續內存塊
    uint32_t          nNumUsed;
    uint32_t          nNumOfElements;
    uint32_t          nTableSize;
    uint32_t          nInternalPointer;
    zend_long         nNextFreeElement;
    dtor_func_t       pDestructor;
};
  • arData 指向 **Bucket 陣列**,每個 Bucket 佔 **32 字節**(64 位)。
  • Bucket 存儲 key(整數或字符串指針)、value(zval)、哈希值、鏈表指針。
  • 鍵為字符串時,字符串內容單獨分配(zend_string,含引用計數)。

3. 單元素邊際成本估算

組件大小說明
Bucket32 B固定
zval (value)16 B整數直接內嵌
zend_string (key)~48 B僅字符串鍵;整數鍵無此開銷
HashTable 擴容預留~8 B負載因子 0.75,平均分攤
小計 (整數鍵)~56 B接近實測 66 B(含 GC、分配器元數據)
小計 (字符串鍵)~104 B+含鍵字符串分配

三、多維數組的記憶體爆炸

<?php
// 一維 10k
$a = array_fill(0, 10000, 1);           // ~0.66 MB

// 二維 10k × 10
$b = [];
for ($i=0; $i<10000; $i++) {
    $b[$i] = array_fill(0, 10, 1);
}                                       // 理論 ~6.6 MB,實測 ~13 MB (2倍)

// 三維 [10k, 5, 2]
$c = [];
for ($i=0; $i<10000; $i++) {
    for ($j=0; $j<5; $j++) {
        $c[$i][$j] = [1, 2];
    }
}                                       // 實測 ~23 MB (遠超理論 13 MB)

原因:每個子數組都是獨立 HashTable(56 B 固定頭 + Bucket 陣列),大量小數組的元數據開銷主導內存。三維數組 10k × 5 = 5 萬個子數組,光 HashTable 頭部就近 2.8 MB。

四、優化策略

  1. 扁平化:用一維數組 + 計算索引 $idx = $i * WIDTH + $j,消除子數組開銷。
  2. 用 SplFixedArray:僅支援整數鍵、固定長度,內存約為陣列 1/3,速度更快。
  3. 避免大量小數組:如 CSV 導入,直接生成大一維數組或流式處理,勿逐行 [] 推入子數組。
  4. 生成器:yield 逐行產出,避免全量駐留內存。
  5. 序列化存儲:非熱數據 serialize() / igbinary 存入 Redis/文件,用時反序列化。

五、空數組最小開銷

$empty = [];  // ~104 B (zval 16 + HashTable 56 + arData 指針 8 + 分配器對齊)
$arr = [$empty]; // 外層數組再 +56 B 含此元素

結論:PHP 數組適合「中小規模、結構靈活」場景;大規模、高性能、固定結構請改用 SplFixedArray、扁平陣列或專用擴展(如 php-ds、FFI 封裝 C 結構)。

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