首頁  >  文章  >  後端開發  >  關於php數組記憶體利用率低以及弱型別的實例講解

關於php數組記憶體利用率低以及弱型別的實例講解

黄舟
黄舟原創
2017-08-10 10:19:111089瀏覽

本篇文章主要介紹了PHP數組記憶體利用率低和弱型別詳細解讀,具有一定的參考價值,有興趣的小夥伴們可以參考一下。

這兩天任務提前完成,可以喘口氣沉澱一下,深入學習PHP。其實本來是想了解PHP效能最佳化相關的東西,但被網路上的一句「PHP數組記憶體利用率低,C語言100MB的記憶體數組,PHP裡需要1G」驚到了。 PHP真的這麼耗記憶體麼?於是藉此機會了解了PHP的資料類型實現方式。

先來做個測驗:


##

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

所得結果:

    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 &#39;argv:&#39;, ($end - $start )/10000,&#39;byte&#39; , &#39;<br>&#39;;

 輸出為:

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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn