PHP數組內存利用率低和弱類型解讀

這兩天任務提前完成,可以喘口氣沉淀一下,深入學習學習PHP。其實本來是想了解一下PHP性能優化相關的東西,但被網上的一句“PHP數組內存利用率低,C語言100MB的內存數組,PHP里需要1G”驚到了。PHP真的這么耗內存么?于是借此機會了解了PHP的數據類型實現方式。
先來做個測試:

<?php  
    echo memory_get_usage() , '<br>';  
    $start = memory_get_usage();  
    $a = Array();  
    for ($i=0; $i<1000; $i++) {  
      $a[$i] = $i + $i;  
    }  
    $end =  memory_get_usage();  
    echo memory_get_usage() , '<br>';  
    echo 'argv:', ($end - $start)/1000 ,'bytes' , '<br>';  

所得結果:

    353352
    437848
    argv:84.416bytes

1000個元素的整數數組耗費內存(437848 - 353352)字節,約合82KB,也就是說每個元素所占內存84字節。在C語言中,一個int占位是4字節,整體相差了20倍。
但是網上又說memery_get_usage()返回的結果不全是數組占用,還包括PHP本身的一些結構,因此,換種方式,采用PHP內置函數生成數組試試:

<?php  
    $start = memory_get_usage();  
    $a = array_fill(0, 10000, 1);  
    $end = memory_get_usage(); //10k elements array;  
    echo 'argv:', ($end - $start )/10000,'byte' , '<br>';  

輸出為:

  argv:54.5792byte

比剛才略好,但也54字節,確實差了10倍左右。
究其原因,還得從PHP的底層實現說起。PHP是一種弱類型的語言,不分int,double,string之類的,統一一個'$'就能解決所有問題。PHP底層由C語言實現,每個變量都對應一個zval結構,其詳細定義為:

typedef struct _zval_struct zval;  
struct _zval_struct {  
    /* Variable information */  
    zvalue_value value;     /* The value 1 12字節(32位機是12,64位機需要8+4+4=16) */  
    zend_uint refcount__gc; /* The number of references to this value (for GC) 4字節 */  
    zend_uchar type;        /* The active type 1字節*/  
    zend_uchar is_ref__gc;  /* Whether this value is a reference (&) 1字節*/  
}; 

PHP使用union結構來存儲變量的值,zval中zvalue_value類型的value變量即為一個union,定義如下:

typedef union _zvalue_value {  
    long lval;                  /* long value */  
    double dval;                /* double value */  
    struct {                    /* string value */  
        char *val;  
        int len;  
    } str;   
    HashTable *ht;              /* hash table value */  
    zend_object_value obj;      /*object value */  
} zvalue_value;  

union類型占用內存的大小有其最大的成員所占的數據空間決定。在zvalue_value中,str結構體的int占4字節,char指針占4字節,故整個zvalue_value所占內存為8字節。
zval的大小即為8 + 4 + 1 + 1 = 14字節。
注意到zvalue_value中還有一個HashTable是做什么的?zval中,數組、字符串和對象還需要另外的存儲結構,數組的存儲結構即為HashTable。
HashTable定義給出:

typedef struct _hashtable {  
     uint nTableSize; //表長度,并非元素個數  
     uint nTableMask;//表的掩碼,始終等于nTableSize-1  
     uint nNumOfElements;//存儲的元素個數  
     ulong nNextFreeElement;//指向下一個空的元素位置  
     Bucket *pInternalPointer;//foreach循環時,用來記錄當前遍歷到的元素位置  
     Bucket *pListHead;  
     Bucket *pListTail;  
     Bucket **arBuckets;//存儲的元素數組  
     dtor_func_t pDestructor;//析構函數  
     zend_bool persistent;//是否持久保存。從這可以發現,PHP數組是可以實現持久保存在內存中的,而無需每次請求都重新加載。  
     unsigned char nApplyCount;  
     zend_bool bApplyProtection;  
} HashTable; 

除了幾個記錄table大小,所含元素數量的屬性變量外,Bucket被多次使用到,Bucket是如何定義的:

typedef struct bucket {  
     ulong h; //數組索引  
     uint nKeyLength; //字符串索引的長度  
     void *pData; //實際數據的存儲地址  
     void *pDataPtr; //引入的數據存儲地址  
     struct bucket *pListNext;  
     struct bucket *pListLast;  
     struct bucket *pNext; //雙向鏈表的下一個元素的地址  
     struct bucket *pLast;//雙向鏈表的下一個元素地址  
     char arKey[1]; /* Must be last element */  
} Bucket; 

有點像一個鏈表,Bucket就像是一個鏈表節點,有具體的數據和指針,而HashTable就是一個array,保存著一串Bucket元素。PHP中多維數組的實現,不過就是Bucket里面存著另一個HashTable罷了。
算一算HashTable需要占用39個字節,Bucket需要33個字節。一個空的數組就需要占用14 + 39 + 33 = 86個字節。Bucket 結構需要 33 個字節,鍵長超過四個字節的部分附加在 Bucket 后面,而元素值很可能是一個 zval 結構,另外每個數組會分配一個由 arBuckets 指向的 Bucket 指針數組, 雖然不能說每增加一個元素就需要一個指針,但是實際情況可能更糟。這么算來一個數組元素就會占用 54 個字節,與上面的估算幾乎一樣。
從空間的角度來看,小型數組平均代價較大,當然一個腳本中不會充斥數量很大的小型數組,可以以較小的空間代價來獲取編程上的快捷。但如果將數組當作容器來使用就是另一番景象了,實際應用經常會遇到多維數組,而且元素居多。比如10k個元素的一維數組大概消耗540k內存,而10k x 10 的二維數組理論上只需要 6M 左右的空間,但是按照 memory_get_usage 的結果則兩倍于此,[10k,5,2]的三維數組居然消耗了23M,小型數組確實是劃不來的。
PHP數組內存利用率低的原因,講到這里,接下來的文章將解讀PHP數組操作的具體實現。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,733評論 18 399
  • 初冬的暖陽 開在飯后的半晌 久違的明朗 喚醒了多少心底的遠方? 柔暖的日光 恩寵了饑寒的滄桑 跳躍著 明燦燦的金黃...
    MaryMargaret閱讀 374評論 3 2
  • 【手寫愛情繪本4.0】時光不僅給我帶來些重要的東西,關鍵帶來了你。努力想讓你微笑,卻總以配角的姿態不那么引你注意,...
    主播亞東閱讀 369評論 0 3
  • 此消彼長 此消彼長講得是能量守衡,總量不變。 當動能達到極限時,勢能為零,當勢能最大時,動能達到極限。這就是機械能...
    一枚冰兒閱讀 399評論 0 0