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:测试