PHP源码分析(内存管理)

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的整数倍,

发布了83 篇原创文章 · 获赞 87 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/ligupeng7929/article/details/90208140