baiyanphp
所有視頻:https://segmentfault.com/a/11...node
源視頻地址:http://replay.xesv5.com/ll/24...redis
typedef unsigned char zend_uchar; struct _zval_struct { zend_value value; /* 存儲變量的zhi*/ union { struct { ZEND_ENDIAN_LOHI_4( //大小端問題,詳情看"PHP內存管理3筆記」 zend_uchar type, //注意這裏就是存放變量類型的地方,char類型 zend_uchar type_flags, //類型標記 zend_uchar const_flags, //是不是常量 zend_uchar reserved) //保留字段 } v; uint32_t type_info; } u1; union { uint32_t next; /* 數組模擬鏈表,在下文鏈地址法解決哈希衝突時使用 */ uint32_t cache_slot; /* literal cache slot */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ uint32_t access_flags; /* class constant access flags */ uint32_t property_guard; /* single property guard */ uint32_t extra; /* not further specified */ } u2; };
/* regular data types */ #define IS_UNDEF 0 #define IS_NULL 1 #define IS_FALSE 2 #define IS_TRUE 3 #define IS_LONG 4 #define IS_DOUBLE 5 #define IS_STRING 6 #define IS_ARRAY 7 #define IS_OBJECT 8 #define IS_RESOURCE 9 #define IS_REFERENCE 10 /* constant expressions */ #define IS_CONSTANT 11 #define IS_CONSTANT_AST 12 /* fake types */ #define _IS_BOOL 13 #define IS_CALLABLE 14 #define IS_ITERABLE 19 #define IS_VOID 18 /* internal types */ #define IS_INDIRECT 15 #define IS_PTR 17 #define _IS_ERROR 20
typedef union _zend_value { zend_long lval; //存整型值 double dval; //存浮點值 zend_refcounted *counted; //存引用計數值 zend_string *str; //存字符串值 zend_array *arr; //存數組值 zend_object *obj; //存對象值 zend_resource *res; //存資源值 zend_reference *ref; //存引用值 zend_ast_ref *ast; //存抽象語法樹 zval *zv; //內部使用 void *ptr; //不肯定類型,取出來以後強轉 zend_class_entry *ce; //存類 zend_function *func;//存函數 struct { uint32_t w1; uint32_t w2; } ww; //這個union一共8B,這個結構體每一個字段都是4B,由於全部聯合體字段共用一塊內存,故至關於取了一半的union } zend_value;
<?php $a = 1; echo $a; $b = 1.1; echo $b; $c = "hello"; echo $c; $d = [1,2,3]; echo $d;
首先思考一個問題,若是讓咱們本身基於C語言設計一個zend_string,應該如何設計?算法
struct _zend_string { zend_refcounted_h gc; zend_ulong h; /* 冗餘的hash值 */ size_t len; char val[1]; };
struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar consistency) } v; uint32_t flags; } u; uint32_t nTableMask; Bucket *arData; //實際存儲數組元素的bucket uint32_t nNumUsed; uint32_t nNumOfElements; uint32_t nTableSize; uint32_t nInternalPointer; zend_long nNextFreeElement; dtor_func_t pDestructor; }; typedef struct _zend_array HashTable;
typedef struct _Bucket { zval val; //元素的值 zend_ulong h; //冗餘的哈希值 zend_string *key; //元素的key } Bucket;
nIndex = h | nTableMask
而後在slot的對應空間內存上第一個bucket對應的索引下標,而後將元素存入對應索引下標的bucket數組中。查找過程也是相似的(下面會細講),它們都是O(1)的時間複雜度,可是這樣就會出現哈希衝突,解決哈希衝突一般有兩種算法:express
比較經常使用的是鏈地址法,但若是同一個hash值上的鏈表過長,會把同一個hash值上的全部鏈表節點都遍歷一遍,時間複雜度會退化爲O(n)。PHP5中有一個漏洞,攻擊者不斷讓你的鏈表變長,使得數組查詢變慢,不斷消耗服務器性能,最終QPS會降低的很是之快。要解決鏈地址法的哈希衝突所帶來的性能降低問題,有以下思路:segmentfault
$a['foo'] = 1; $a[] = 2; $a['s'] = 3; $a['x'] = 4;
這是一個很是簡單的幾個數組賦值語句,咱們具體看一下它們的插入過程:數組
- $a['foo'] = 1;這裏的key和value若是是字符串,須要單獨在zend_string結構中存儲其真實的值和長度,這裏作了簡化。
- $a[] = 2;
- $a['s'] = 3; 這裏注意須要先修改索引數組,保證索引數組中第一個指向的bucket數組單元是最後插入bucket數組的值(頭插法),而且修改val.u2.next指針(由於全部val都是zval類型),指向上一個具備相同hash值的元素。
- $a['x'] = 4;同上
再來看一個數組查詢過程,例如訪問$a['s']的值:服務器
下面咱們再看一段PHP代碼:數據結構
<?php for ($i = 0; $i<=200000; $i++){ $arr1[$i] = $i; } for($i = 200000; $i>=0; $i--) { $arr2[$i] = $i; }
那麼爲何會這樣呢?先看兩個概念:packed array與hash array函數
packed array的特色:
$arr = [ 0 => 0, 1 => 1, 2 => 2, 100000 => 3 ];