麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 編程 > PHP > 正文

PHP數組和值到底有多大

2020-03-22 19:42:42
字體:
來源:轉載
供稿:網友
這篇文章是關于PHP 5的內存使用情況。對于本文所述的情況,PHP 7中的內存使用量大約低3倍。

在這篇文章中,我想以下面的腳本為例來研究PHP數組(以及一般的值)的內存使用情況,該腳本創建了100000個惟一的整數數組元素,并測量了結果的內存使用情況:

$startMemory = memory_get_usage();$array = range(1, 100000);echo memory_get_usage() - $startMemory, bytes 

你希望它是多少?簡單來說,一個整數是8字節(在64位unix機器上使用long類型),您得到100,000個整數,因此顯然需要800000字節。

現在嘗試運行上面的代碼。這就得到了14649024字節。是的,你沒聽錯,是13.97 MB,比我們估計的多18倍。

那么,18的額外因數是怎么來的呢?

總結

對于那些不想知道整個故事的人,這里有一個涉及到的不同組件的內存使用的快速總結:

 | 64 bit | 32 bit---------------------------------------------------zval | 24 bytes | 16 bytes+ cyclic GC info | 8 bytes | 4 bytes+ allocation header | 16 bytes | 8 bytes===================================================zval (html' target='_blank'>value) total | 48 bytes | 28 bytes===================================================bucket | 72 bytes | 36 bytes+ allocation header | 16 bytes | 8 bytes+ pointer | 8 bytes | 4 bytes===================================================bucket (array element) total | 96 bytes | 48 bytes===================================================total total | 144 bytes | 76 bytes

上述數字將根據您的操作系統、編譯器和編譯選項的不同而有所不同。例如,如果您使用調試或線程安全來編譯PHP,您將得到不同的數字。但是我認為上面給出的大小是您將在Linux上的PHP 5.3的64位生產版本中看到的大小。

如果你用這144字節乘以100000個元素,你會得到14400000字節,也就是13.73 MB,這與實際數字非常接近——剩下的大部分都是未初始化bucket的指針,但是我將在后面討論這個問題。

現在,如果您想對上面提到的值進行更詳細的分析,請繼續閱讀:)

zvalue_value聯盟

首先看看PHP是如何存儲值的。正如您所知道的,PHP是一種弱類型語言,因此它需要某種方式在各種類型之間快速切換。PHP為此使用union,它在zend中定義如下。

typedef union _zvalue_value { long lval; // For integers and booleans double dval; // For floats (doubles) struct { // For strings char *val; // consisting of the string itself int len; // and its length } str; HashTable *ht; // For arrays (hash tables) zend_object_value obj; // For objects} zvalue_value;

如果您不知道C,這不是一個問題,因為代碼非常簡單:union是一種使某些值可以作為各種類型訪問的方法。例如,如果您執行zvalue_value- lval,您將得到一個被解釋為整數的值。另一方面,如果您使用zvalue_value- ht,則該值將被解釋為指向哈希表(即數組)的指針。

但我們不要在這里講太多。對我們來說,唯一重要的是一個union的大小等于它的最大組件的大小。這里最大的組件是字符串結構體(zend_object_value結構體的大小與str結構體相同,但為了簡單起見,我將省略它)。string struct存儲一個指針(8字節)和一個整數(4字節),總共是12字節。由于內存對齊(12字節的結構并不酷,因為它們不是64位/ 8字節的倍數),結構的總大小將是16字節,這也是union作為一個整體的大小。

現在我們知道,由于PHP的動態類型,每個值不需要8字節,而是16字節。乘以100000個值得到1600000字節,也就是1.53 MB,但是實際的值是13.97 MB,所以我們還不能得到它。

zval的結構

這非常符合邏輯——union只存儲值本身,但是PHP顯然還需要存儲類型和一些垃圾收集信息。保存此信息的結構稱為zval,您可能已經聽說過它。關于PHP為什么需要它的更多信息,我建議閱讀Sara Golemon的一篇文章。無論如何,這個結構的定義如下:

struct _zval_struct { zvalue_value value; // The value zend_uint refcount__gc; // The number of references to this value (for GC) zend_uchar type; // The type zend_uchar is_ref__gc; // Whether this value is a reference ( )};

結構的大小由其組件的大小之和決定:zvalue_value為16字節(如上所計算),zend_uint為4字節,zend_uchars為1字節。總共是22字節。由于內存對齊,實際大小將是24字節。

因此,如果我們存儲100,000個元素a 24字節,那么總共就是2400000,也就是2.29 MB,差距正在縮小,但是實際值仍然是原來的6倍多。

循環收集器(從PHP 5.3開始)

PHP 5.3引入了一個新的循環引用垃圾收集器。為此,PHP必須存儲一些額外的數據。我不想在這里解釋這個算法是如何工作的,你可以在手冊的鏈接頁上讀到。對于我們的大小計算來說,重要的是PHP將把每個zval包裝成zval_gc_info:

typedef struct _zval_gc_info { zval z; union { gc_root_buffer *buffered; struct _zval_gc_info *next; } u;} zval_gc_info;

正如您所看到的,Zend只在它上面添加了一個union,它由兩個指針組成。希望您還記得,union的大小就是它最大的組件的大小:兩個union組件都是指針,因此它們的大小都是8字節。所以union的大小也是8字節。

如果我們把它加到24字節上面我們已經有32字節了。再乘以100000個元素,我們得到的內存使用量是3。05 MB。

Zend MM分配器

C與PHP不同,它不為您管理內存。你需要自己記錄你的分配。為此,PHP使用了專門針對其需要優化的自定義內存管理器:Zend內存管理器。Zend MM基于Doug Lea的malloc,并添加了一些PHP特有的優化和特性(如內存限制、每次請求后清理等)。

這里對我們來說重要的是,MM為通過它完成的每個分配添加一個分配頭。定義如下:

typedef struct _zend_mm_block { zend_mm_block_info info;#if ZEND_DEBUG unsigned int magic;# ifdef ZTS THREAD_T thread_id;# endif zend_mm_debug_info debug;#elif ZEND_MM_HEAP_PROTECTION zend_mm_debug_info debug;#endif} zend_mm_block;typedef struct _zend_mm_block_info {#if ZEND_MM_COOKIES size_t _cookie;#endif size_t _size; // size of the allocation size_t _prev; // previous block (not sure what exactly this is)} zend_mm_block_info;

如您所見,這些定義充斥著大量的編譯選項檢查。如果你用堆保護,多線程,調試和MM cookie來構建PHP,那么如果你用堆保護,多線程,調試和MM cookie來構建PHP,那么如果你用堆保護,多線程,調試和MM cookie來構建PHP,那么分配頭文件會更大。

對于本例,我們假設所有這些選項都是禁用的。在這種情況下,只剩下兩個size_ts _size和_prev。size_t有8個字節(在64位上),所以分配頭的總大小是16個字節——并且在每個分配上都添加了這個頭。

現在我們需要再次調整zval大小。實際上,它不是32字節,而是48字節,這是由分配頭決定的。乘以100000個元素是4。58 MB,實際值是13。97 MB,所以我們已經得到了大約三分之一的面積。

Buckets

到目前為止,我們只考慮單個值。但是PHP中的數組結構也會占用大量空間:“數組”在這里實際上是一個不合適的術語。PHP數組實際上是散列表/字典。那么哈希表是如何工作的呢?基本上,對于每個鍵,都會生成一個散列,該散列用作“real”C數組的偏移量。由于哈希值可能會沖突,具有相同哈希值的所有元素都存儲在鏈表中。當訪問一個元素時,PHP首先計算散列,查找正確的bucket并遍歷鏈接列表,逐個元素比較確切的鍵。bucket的定義如下:

typedef struct bucket { ulong h; // The hash (or for int keys the key) uint nKeyLength; // The length of the key (for string keys) void *pData; // The actual data void *pDataPtr; // ??? What s this ??? struct bucket *pListNext; // PHP arrays are ordered. This gives the next element in that order struct bucket *pListLast; // and this gives the previous element struct bucket *pNext; // The next element in this (doubly) linked list struct bucket *pLast; // The previous element in this (doubly) linked list const char *arKey; // The key (for string keys)} Bucket;

正如您所看到的,需要存儲大量數據才能獲得PHP使用的抽象數組數據結構(PHP數組同時是數組、字典和鏈表,這當然需要大量信息)。單個組件的大小為無符號long為8字節,無符號int為4字節,指針為7乘以8字節。總共是68。添加對齊,得到72字節。

像zvals這樣的bucket需要在頭部分配,因此我們需要再次為分配頭添加16個字節,從而得到88個字節。我們還需要在“real”C數組中存儲指向這些Bucket的指針(Bucket ** arbucket;)我上面提到過,每個元素增加8個字節。所以總的來說,每個bucket需要96字節的存儲空間。

如果每個值都需要一個bucket,那么bucket是96字節,zval是48字節,總共144字節。對于100000個元素,也就是14400000字節,即13.73 MB。

神秘的解決。

等等,還有0.24 MB !

最后的0.24 MB是由于未初始化的存儲bucket造成的:理想情況下,存儲bucket的實際C數組的大小應該與存儲的數組元素的數量大致相同。通過這種方式,沖突最少(除非希望浪費大量內存)。但是PHP顯然不能在每次添加元素時重新分配整個數組——這將非常緩慢。相反,如果內部bucket數組達到限制,PHP總是將其大小加倍。所以數組的大小總是2的冪。

在我們的例子中是2 ^ 17 = 131072。但是我們只需要100000個bucket,所以我們留下31072個bucket沒有使用。這些bucket不會被分配(因此我們不需要花費全部的96字節),但是bucket指針(存儲在內部桶數組中的那個)的內存仍然需要分配。所以我們另外使用8字節(一個指針)* 31072個元素。這是248576字節或0.23 MB,與丟失的內存匹配。(當然,這里仍然缺少一些字節,但是我不想在這里介紹。比如哈希表結構本身,變量等等)

神秘真的解決了。

這告訴我們什么?

PHP不是c,這就是所有這些告訴我們的。您不能期望像PHP這樣的超級動態語言具有與C語言相同的高效內存使用。你不能。

但是,如果您確實想節省內存,可以考慮使用SplFixedArray處理大型靜態數組。

看看這個修改后的腳本:

$startMemory = memory_get_usage();$array = new SplFixedArray(100000);for ($i = 0; $i 100000; ++$i) { $array[$i] = $i;echo memory_get_usage() - $startMemory, bytes 

它基本上做的是相同的事情,但是如果運行它,您會注意到它只使用了“5600640字節”。這是每個元素56字節,因此比普通數組使用的每個元素144字節要少得多。這是因為一個固定的數組不需要bucket結構:所以它只需要每個元素一個zval(48字節)和一個指針(8字節),從而得到觀察到的56字節。

以上就是PHP數組和值到底有多大的詳細內容,PHP教程

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: jizzjizzjizz少妇| 在线成人一区二区 | 美女久久 | 国产资源在线观看 | wwwxxx视频| 国产免费小视频在线观看 | 久久网站热最新地址4 | 狠狠久久伊人中文字幕 | 久成人| 久草在线观看福利 | 国产精品久久久久久久久久久天堂 | 美女露100%无遮挡 | 久草干 | 91麻豆精品国产91久久久无需广告 | 欧美日本另类 | 欧美精品色精品一区二区三区 | 国产免费观看a大片的网站 欧美成人一级 | 在线播放一区二区三区 | 男女羞羞视频在线观看免费 | 曰韩黄色片 | 羞羞视频一区二区 | 国产毛片自拍 | 视频一区二区三区视频 | 精品一区二区三区中文字幕老牛 | 91网站在线播放 | 欧美a∨亚洲欧美亚洲 | 性少妇chinesevideo | 色无极影院亚洲 | 亚洲午夜久久久精品一区二区三区 | aaaaaaa毛片| 日本a∨精品中文字幕在线 狠狠干精品视频 | 日韩视频在线视频 | 天堂在线资源av | 久久在草 | 欧美视频99| 黄色特级一级片 | 久久蜜桃香蕉精品一区二区三区 | 欧美色视频免费 | 亚洲小视频在线观看,com | 精品在线观看一区 | 久久国产在线观看 |