void *ptr=_emalloc(size);
_efree(*ptr) //释放内存的时候只传入ptr,并没有传入释放内存大小
当我们申请一个size大小的内存的时候,我们多申请一些存起来,下次用户申请的时候,直接给出相应大小的内存即可,这样减少了用户态和内核态的切换,提高效率,内存回收的时候需要知道这个内存属于哪个内存页page,属于哪个chunk,以便回收
chunk内存会进行内存的预分配,使用mmap分配chunk
内存分类: Small(30种规格)(size<=3kB)
Large(3kb<size<=2MB-4KB)//4K的整数倍
Huge(size>2MB-4KB) //如果申请3MB内存,肯定返回比3MB大且2MB倍数内存
比如申请大小为7的内存,返回的大小为8的内存
内存分配流程:
如果有现成已经满足需要的small内存就直接返回,否则执行small_slow函数
调用mmap会申请大的内存
当调用samll_slow会先去large内存里面取一个page,如果page用完了,会从mm_chunk_alloc里面申请一个chunk
在small的30个规格中:
比如申请一个size是7的,我是直接找到包含7的最小的size,比如申请一个size=8的话,直接申请一个page,切割成了512个,其中一份返回回去,剩下的511个挂在
申请Small内存的时候:1、要找到size最小的规格 2、然后在chunk上申请一个page,如果一个page不够用的话,申请3个page,分成四份,把其中一份返回给用户,剩下的保存在mm_heap
回顾下free_slot字段的定义:
zend_mm_free_slot *free_slot[ZEND_MM_BINS];
struct zend_mm_free_slot {
zend_mm_free_slot *next_free_slot;
}
mm_heap里面有一个free_slot,是一个数组,挂了0-29,比如我们申请512的8字节,把第一个返回,剩下的511个按照链表的方式存起来
思考:为什么最小是8字节内存块
可以看出空闲内存链表的每个节点都是一个zend_mm_free_slot结构体,其只有一个next指针字段;因为8个字节恰好放next指针,因为需要维护链表,这里有一个next指针,需要占用内存的
思考:对于8字节大小的内存块,其next指针就需要占8字节的空间,那用户的数据存储在哪里呢?
答案:free_slot是small内存的空闲链表,空闲指的是未分配内存,此时是不需要存储其他数据的;当分配给用户时,此节点会从空闲链表删除,也就不需要维护next指针了;用户可以在8字节里存储任何数据;
思考:如何快速找到包含所需内存大小的最小规格呢?
答案:
Chunk的内存对齐
1、怎么定位快要释放内存是多大的
对任意地址p,如何计算页号? --2M字节对齐
chunk的大小为2M,首地址高43位为数字,低21位为0,他的地址为2M的整数倍
虚拟地址:chunk的首地址和chunk的偏移量进行构成
如何确保一个chunk的地址时2M字节对齐的呢?
我们申请内存的时候是由内存页来管理的,我们malloc出来的内存地址一定是4k的整数倍,