baiyanphp
所有視頻:https://segmentfault.com/a/11...redis
原視頻地址:http://replay.xesv5.com/ll/24...算法
本筆記中部分圖片截自視頻中的片斷,圖片版權歸視頻原做者全部。express
#include <stdlib.h> int main() { void *ptr = malloc(8); return 1; }
操做系統中有一個記錄空閒內存地址的鏈表。當操做系統收到程序的申請時,就會遍歷該鏈表,而後就尋找第一個空間大於所申請空間的堆結點,而後就將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序。malloc函數的實質體如今,它有一個將可用的內存塊鏈接爲一個長長的列表的所謂空閒鏈表(Free List)。調用malloc函數時,它沿鏈接表尋找一個大到足以知足用戶請求所須要的內存塊(根據不一樣的算法而定(將最早找到的不小於申請的大小內存塊分配給請求者,將最合適申請大小的空閒內存分配給請求者,或者是分配最大的空閒塊內存塊)。而後,將該內存塊一分爲二(一塊的大小與用戶請求的大小相等,另外一塊的大小就是剩下的字節)。接下來,將分配給用戶的那塊內存傳給用戶,並將剩下的那塊(若是有的話)返回到鏈接表上。調用free函數時,它將用戶釋放的內存塊鏈接到空閒鏈上。到最後,空閒鏈會被切成不少的小內存片斷,若是這時用戶申請一個大的內存片斷,那麼空閒鏈上可能沒有能夠知足用戶要求的片斷了。因而,malloc函數請求延時,並開始在空閒鏈上翻箱倒櫃地檢查各內存片斷,對它們進行整理,將相鄰的小空閒塊合併成較大的內存塊。若是沒法得到符合要求的內存塊,malloc函數會返回NULL指針,所以在調用malloc動態申請內存塊時,必定要進行返回值的判斷。
若是不想對齊,有以下解決方案:segmentfault
代碼層面:在struct後加關鍵字,例如redis中的sds簡單動態字符串的實現:數組
struct __attribute__ ((packed)) sdshdr16 { uint16_t len; uint16_t alloc; unsigned char flags; char buf[]; }
#define _BIN_DATA_SIZE(num, size, elements, pages, x, y) size, static const uint32_t bin_data_size[] = { ZEND_MM_BINS_INFO(_BIN_DATA_SIZE, x, y) };
struct _zval_struct { zend_value value; union u1; union u2; };
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;
... union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar type, /* 在這裏用unsigned char存放PHP變量值的類型 */ zend_uchar type_flags, zend_uchar const_flags, zend_uchar reserved) /* call info for EX(This) */ } v; uint32_t type_info; } u1; ...
/* 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
struct _zend_string { zend_refcounted_h gc; /*引用計數,與垃圾回收相關,暫不展開*/ zend_ulong h; /* 冗餘的hash值,計算數組key的哈希值時避免重複計算*/ size_t len; /* 長度 */ char val[1]; /* 柔性數組,真正存放字符串值 */ };
<?php $a = "string" . time("Y-m-d"); echo $a; $b = $a; echo $a; echo $b; $b = "new string"; echo $a; echo $b;
那麼爲何$b所指向的zend_string的refcount是0呢,咱們先給PHP中的字符串分個類:安全
<?php $a = "hello" . time("Y-m-d"); //臨時字符串,由於time()會隨時間變化而變化 $b = "hello world"; //常量字符串
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; //數組大小減一,用來作或運算,packed array初始值是-2,hash array初始值是-8 Bucket *arData; //指針,指向實際存儲數組元素的bucket uint32_t nNumUsed; //使用了多少bucket,可是unset的時候這個值不減小 uint32_t nNumOfElements; //真正有多少元素,unset的時候會減小 uint32_t nTableSize; //bucket的個數 uint32_t nInternalPointer; zend_long nNextFreeElement; //支持$arr[] = 1;語法,沒插入1個元素就會遞增1 dtor_func_t pDestructor; }; typedef struct _zend_array HashTable;
typedef struct _zend_refcounted_h { uint32_t refcount; /* 引用計數 */ union { struct { ZEND_ENDIAN_LOHI_3( zend_uchar type, zend_uchar flags, /* used for strings & objects */ uint16_t gc_info) /* keeps GC root number (or 0) and color */ } v; uint32_t type_info; } u; } zend_refcounted_h;
typedef struct _Bucket { zval val; //元素的值,注意這裏直接存了zval而不是一個zval指針 zend_ulong h; //冗餘的哈希值,避免重複計算哈希值 zend_string *key; //元素的key值,指向一個zend_string結構體 } Bucket;
看一個簡單的數組查找過程:數據結構
PHP7中的數組分爲兩種:packed array與hash array。函數
packed array:測試