从zval看PHP变量 liangdong@smzdm.com
目录 变量拷贝是否影响性能? 万能的array 为何脚本占用大量内存?
变量拷贝是否影响性能?
安装XDEBUG扩展
共享内存,相安无事 多个变量指向同一个底层数组
我要修改,当机立断 写时复制,底层数组发生拷贝,变量变更指向
zend_string { refcount 引用计数 len 字符串长度 val[] 字符串内存 }; type=IS_STRING zval { type 变量类型 value* 底层内存 } type=IS_ARRAY zend_array { refcount 引用计数 arData* 数组内存 nNumUsed 已用槽位 nNumOfElements 元素个数 nTableSize 总槽位数量 };
万能的array
packed array $a = ['a', 'b', 'c', 'd']; unset($a[1]); 'a' undef 'c' zend_array { arData* 数组内存 nNumUsed 已用槽位 = 4 nNumOfElements 元素个数 = 3 nTableSize 总槽位数量 = 8 }; echo $a[1]; packed array 顺序追加而成的数组,读写时间复杂度O(1)
HASH ARRAY $a = [ 'hello', 'b' => 'php', 'c' => 'world' ]; unset($a['b']); next=2 hello undef world undef -1 -1 -1 zend_array { arData* 数组内存 nNumUsed 已用槽位 = 4 nNumOfElements 元素个数 = 3 nTableSize 总槽位数量 = 4 }; 假设: hash('c') == 0 hash('b') == 1 HASH ARRAY 键值字典OR不适合PACKED存储,则采用HASH存储,读写时间复杂度为O(lgN)
zend_mm_huge_list(>2MB) real_size limit (memory_limit) free_slot[] main_chunk* cached_chunks* huge_list* zend_mm_heap zend_mm_chunk(2MB) zend_mm_page(4K) zend_mm_free_slot(<4K) ... ... ... next zend_mm_huge_list(>2MB) 2MB next 8MB 为何脚本占用大量内存?
small内存申请 zend_mm_heap chunk=2MB 512*8B=4K free_slot[0] ... [1] [2] [3] ... [29] zend_mm_heap chunk=2MB 512*8B=4K ... 8 8 8 8 8 ... SRUN chunk=2MB ... 3072B 3072B 3072B 3072B SRUN(3page,4elements) small内存申请 <=3072B走SRUN分配,不同size对应不同的预分配方案
LARGE内存申请 main_chunk* zend_mm_heap page=4K chunk=2MB ... 8KB LRUN(2page,1elment) next chunk=2MB struct zend_mm_chunk { free_map // 512bits,记录每个page是否被使用 map[512] // 记录每个page的用途(SRUN OR LRUN, page_count) } LARGE内存申请 >3072B && <2MB走LRUN分配,占用连续的多个page
HUGE内存申请 zend_mm_heap chunk=2MB 4MB huge_list* next 2MB >=2MB走Huge分配,占用连续的多个chunk
small内存释放 zend_mm_heap free_slot[0] [1] [2] chunk=2MB [3] 512*8B=4K ... [29] zend_mm_heap chunk=2MB 512*8B=4K ... 8 8 8 8 8 ... SRUN 8 small内存释放 插回free_slot[n]链表头部
large内存释放 main_chunk* cached_chunk* zend_mm_heap page=4K chunk=2MB ... chunk所有page均归还 LRUN(2page,1elment) 8KB(to free) large内存释放 归还对应若干page,若chunk无剩余page外借,则将chunk放入cached_chunk备用
HUGE内存释放 zend_mm_heap chunk=2MB 4MB huge_list* next 2MB
触发memory_limit step1:遍历free_slots,计算所属SRUN外借slots个数 chunk=2MB 512*8B=4K ... 8 8 8 8 8 ... SRUN step2:遍历free_slots,将无外借的SRUN内的slots从free_slots链表删除 step3:遍历main_chunk,将无外借的SRUN对应的pages标记归还 step4:若step3后,chunk无外借的pages,则释放整个chunk 触发memory_limit 分配过的chunk总内存>memory_limit,则执行zend_mm_gc回收SRUN占用的page
q&a