關於php數組記憶體利用率低以及弱類型的執行個體講解

來源:互聯網
上載者:User
本篇文章主要介紹了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,小型數組確實是划不來的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.