PHP數組實際占用內存大小的分析

我們在前面的php高效寫法提到,盡量不要復制變量,特別是數組。一般來說,PHP數組的內存利用率只有 1/10, 也就是說,一個在C語言里面100M 內存的數組,在PHP里面就要1G。下面我們可以粗略的估算PHP數組占用內存的大小,首先我們測試1000個元素的整數占用的內存:

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

輸出是:

353352
    437848
    522024
    argv:84.416bytes
    argv:84.176

大概了解1000 個元素的整數數組需要占用 82k 內存,平均每個元素占用 84 個字節。而純 C 中整體只需要 4k(一個整型占用4byte * 1000 )。memory_get_usage() 返回的結果并不是全是被數組占用了,還要包括一些 PHP 運行本身分配的一些結構,可能用內置函數生成的數組更接近真實的空間:

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

得到:

argv:54.5792byte
argv:54.5784byte

從這個結果來看似乎一個數組元素大約占用了54個字節左右。

首先看一下32位機C語言各種類型占用的字節:

#include "stdafx.h"  
//#include <stdio.h>  
   
int main() {  
        printf("int:%d\nlong:%d\ndouble:%d\nchar*:%d\nsize_t:%d\n",   
        sizeof(int), sizeof(long),   
        sizeof(double), sizeof(char *),   
        sizeof(size_t));  
    return   0;   
}

1.jpg


int:4
long:4
double:8
har*:4
size_t:4
在PHP中都使用long類型來代表數字,沒有使用int類型
大家都明白PHP是一種弱類型的語言,它不會去區分變量的類型,沒有int float char *之類的概念。
我們看看php在zend里面存儲的變量,PHP中每個變量都有對應的 zval, Zval結構體定義在Zend/zend.h里面,其結構:

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結構來存儲變量的值,即zvalue_value 是一個union,UNION變量所占用的內存是由最大

成員數據空間決定。

f 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;

 最大成員數據空間是struct str,指針占*val用4字節,INT占用4字節,共8字節。

       struct zval占用的空間為8+4+1+1 = 14字節,

      其實呢,在zval中數組,字符串和對象還需要另外的存儲結構,數組則是一個 HashTable:

   HashTable結構體定義在Zend/zend_hash.h.

typedetypedef struct _hashtable {  
    uint nTableSize;//4  
    uint nTableMask;//4  
    uint nNumOfElements;//4  
    ulong nNextFreeElement;//4  
    Bucket *pInternalPointer;   /* Used for element traversal 4*/  
    Bucket *pListHead;//4  
    Bucket *pListTail;//4  
    Bucket **arBuckets;//4  
    dtor_func_t pDestructor;//4  
    zend_bool persistent;//1  
    unsigned char nApplyCount;//1  
    zend_bool bApplyProtection;//1  
#if ZEND_DEBUG  
    int inconsistent;//4  
#endif  
} HashTable;

HashTable 結構需要 39 個字節,每個數組元素存儲在 Bucket 結構中:

typedef struct bucket {  
    ulong h;    /* Used for numeric indexing                4字節 */  
    uint nKeyLength;    /* The length of the key (for string keys)  4字節 */  
    void *pData;        /* 4字節*/  
    void *pDataPtr;         /* 4字節*/  
    struct bucket *pListNext;  /* PHP arrays are ordered. This gives the next element in that order4字節*/  
    struct bucket *pListLast;  /* and this gives the previous element           4字節 */  
    struct bucket *pNext;      /* The next element in this (doubly) linked list     4字節*/  
    struct bucket *pLast;      /* The previous element in this (doubly) linked list     4字節*/  
    char arKey[1];            /* Must be last element   1字節*/  
} Bucket;

Bucket 結構需要 33 個字節,鍵長超過四個字節的部分附加在 Bucket 后面,而元素值很可能是一個 zval 結構,另外每個數組會分配一個由 arBuckets 指向的 Bucket 指針數組, 雖然不能說每增加一個元素就需要一個指針,但是實際情況可能更糟。這么算來一個數組元素就會占用 54 個字節,與上面的估算幾乎一樣。

    一個空數組至少會占用 14(zval) + 39(HashTable) + 33(arBuckets) = 86 個字節,作為一個變量應該在符號表中有個位置,也是一個數組元素,因此一個空數組變量需要 118 個字節來描述和存儲。從空間的角度來看,小型數組平均代價較大,當然一個腳本中不會充斥數量很大的小型數組,可以以較小的空間代價來獲取編程上的快捷。但如果將數組當作容器來使用就是另一番景象了,實際應用經常會遇到多維數組,而且元素居多。比如10k個元素的一維數組大概消耗540k內存,而10k x 10 的二維數組理論上只需要 6M 左右的空間,但是按照 memory_get_usage 的結果則兩倍于此,[10k,5,2]的三維數組居然消耗了23M,小型數組果然是劃不來的。

轉自:http://blog.csdn.net/hguisu/article/details/7376705

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/3064

(0)
s19930811s19930811
上一篇 2015-05-27 10:07
下一篇 2015-05-28 09:41

相關推薦

  • 包管理一:配置本地ISO的yum源

    故事背景:網上找了一個軟件,但是這個軟件需要依賴光盤上面的基礎包,但是這個服務器又不能上網,怎么辦? 方法:這里推薦配置本地ISO的yum源,然后yum localinstall xxx.rpm 1、yum的配置文件說明 配置文件: /etc/yum.conf:為所有倉庫提供公共配置 /etc/yum.repos.d/*.repo:為倉庫的指向提供配置 倉庫…

    Linux干貨 2016-01-05
  • HTTP詳解(3)-http1.0 和http1.1 區別

    翻了下HTTP1.1的協議標準RFC2616,下面是看到的一些它跟HTTP1.0的差別。 1. Persistent Connection持久連接        在HTTP1.0中,每對Request/Response都使用一個新的連接。      …

    Linux干貨 2015-04-04
  • 使用Openssl構建私有CA

    使用Openssl構建私有CA Openssl是SSL的開源實現,是一種安全機密程序,主要用于提高遠程登錄訪問的安全性。也是目前加密算法所使用的工具之一,功能很強大。     Openssl為網絡通信提供安全及數據完整性的一種安全協議,包括了主要的密碼算法、常用的密鑰和證書封裝管理功能(CA)以及SSL協議,并提供了豐…

    Linux干貨 2015-10-07
  • Linux的啟動流程

    Linux的啟動流程大致上如下圖. 現在詳細說明一下每個步驟: 第一階段 當系統啟動時,系統首先會加載BIOS。BIOS的首先會檢查各硬件設備,當檢查完畢沒有問題之后。BIOS會根據設定的BootSequence來尋找可以引導系統的設備。一般而言,Linux是通過磁盤上MBR來引導系統的。 第二階段 MBR是Master Boot Record,是位于磁盤第…

    Linux干貨 2016-02-28
  • 一次簡單的內核編譯(二)

      前言:     此次編譯是繼一次簡單的內核編譯(一)進行操作編譯的,請先查看第一篇再來看此片文章 一、使用busybox代替自己制作的init腳本,實現內核啟動;  1、在這里我們使用靜態編譯busybox,所以需要先安裝glibc-static依賴包,如果不安裝會報錯     2、解…

    Linux干貨 2015-06-01
  • bash通配符和正則表達式元字符部分歸納

    Linux中有各種各樣的字符,而且在不同環境和不同命令之下含義也不同 作為新手,決定先歸納學到的符號,方便后面學習厘清它們之間的關系。 glob 簡化了的正則表達式 bash默認通配符: ? :只匹配一個任意字符; * :匹配零個或多個任意字符;   [^] :方括號及其中^中的取反 [abc]:匹配任何一個列在方括號中的字符(這個例子要么匹配一個…

    Linux干貨 2016-04-11
欧美性久久久久