详解PHP内存池中的存储层

PHP的内存管理器是分层(hierarchical)的。这个管理器共有三层:存储层(storage)、堆(heap)层和 emalloc/efree 层。存储层通过 malloc()、mmap() 等函数向系统真正的申请内存,并通过free()函数释放所申请的内存。

存储层通常申请的内存块都比较大,这里申请的内存大并不是指storage层结构所需要的内存大,只是堆层通过调用存储层的分配方法时,其以段的格式申请的内存比较大,存储层的作用是将内存分配的方式对堆层透明化。

首先看storage层的结构:

 
  1. /*Heapswithuserdefinedstorage*/
  2. typedefstruct_zend_mm_storagezend_mm_storage;
  3. typedefstruct_zend_mm_segment{
  4. size_tsize;
  5. struct_zend_mm_segment*next_segment;
  6. }zend_mm_segment;
  7. typedefstruct_zend_mm_mem_handlers{
  8. constchar*name;
  9. zend_mm_storage*(*init)(void*params);//初始化函数
  10. void(*dtor)(zend_mm_storage*storage);//析构函数
  11. void(*compact)(zend_mm_storage*storage);
  12. zend_mm_segment*(*_alloc)(zend_mm_storage*storage,size_tsize);//内存分配函数
  13. zend_mm_segment*(*_realloc)(zend_mm_storage*storage,zend_mm_segment*ptr,size_tsize);//重新分配内存函数
  14. void(*_free)(zend_mm_storage*storage,zend_mm_segment*ptr);//释放内存函数
  15. }zend_mm_mem_handlers;
  16. struct_zend_mm_storage{
  17. constzend_mm_mem_handlers*handlers;//处理函数集
  18. void*data;
  19. };

内存的分配方式,调用的函数是_zend_mm_storage结构中的处理函数集,而内存是以段的形式表现的。

4种内存方案

PHP在存储层共有4种内存分配方案: malloc,win32,mmap_anon,mmap_zero。默认使用malloc分配内存,如果设置了ZEND_WIN32宏,则为windows版本,调用HeapAlloc分配内存,剩下两种内存方案为匿名内存映射,并且PHP的内存方案可以通过设置变量来修改。

官方说明如下:

The Zend MM can be tweaked using ZEND_MM_MEM_TYPE and ZEND_MM_SEG_SIZE environment variables. Default values are “malloc” and “256K”.Dependent on target system you can also use “mmap_anon”, “mmap_zero” and “win32″ storage managers.

在代码中,对于这4种内存分配方案,分别对应实现了zend_mm_mem_handlers中的各个处理函数。配合代码的简单说明如下:

 
  1. /*使用mmap内存映射函数分配内存写入时拷贝的私有映射,并且匿名映射,映射区不与任何文件关联。*/
  2. #defineZEND_MM_MEM_MMAP_ANON_DSC{"mmap_anon",zend_mm_mem_dummy_init,zend_mm_mem_dummy_dtor,zend_mm_mem_dummy_compact,zend_mm_mem_mmap_anon_alloc,zend_mm_mem_mmap_realloc,zend_mm_mem_mmap_free}
  3. /*使用mmap内存映射函数分配内存写入时拷贝的私有映射,并且映射到/dev/zero。*/
  4. #defineZEND_MM_MEM_MMAP_ZERO_DSC{"mmap_zero",zend_mm_mem_mmap_zero_init,zend_mm_mem_mmap_zero_dtor,zend_mm_mem_dummy_compact,zend_mm_mem_mmap_zero_alloc,zend_mm_mem_mmap_realloc,zend_mm_mem_mmap_free}
  5. /*使用HeapAlloc分配内存windows版本关于这点,注释中写的是VirtualAlloc()toallocatememory,实际在程序中使用的是HeapAlloc*/
  6. #defineZEND_MM_MEM_WIN32_DSC{"win32",zend_mm_mem_win32_init,zend_mm_mem_win32_dtor,zend_mm_mem_win32_compact,zend_mm_mem_win32_alloc,zend_mm_mem_win32_realloc,zend_mm_mem_win32_free}
  7. /*使用malloc分配内存默认为此种分配如果有加ZEND_WIN32宏,则使用win32的分配方案*/
  8. #defineZEND_MM_MEM_MALLOC_DSC{"malloc",zend_mm_mem_dummy_init,zend_mm_mem_dummy_dtor,zend_mm_mem_dummy_compact,zend_mm_mem_malloc_alloc,zend_mm_mem_malloc_realloc,zend_mm_mem_malloc_free}
  9. staticconstzend_mm_mem_handlersmem_handlers[]={
  10. #ifdefHAVE_MEM_WIN32
  11. ZEND_MM_MEM_WIN32_DSC,
  12. #endif
  13. #ifdefHAVE_MEM_MALLOC
  14. ZEND_MM_MEM_MALLOC_DSC,
  15. #endif
  16. #ifdefHAVE_MEM_MMAP_ANON
  17. ZEND_MM_MEM_MMAP_ANON_DSC,
  18. #endif
  19. #ifdefHAVE_MEM_MMAP_ZERO
  20. ZEND_MM_MEM_MMAP_ZERO_DSC,
  21. #endif
  22. {NULL,NULL,NULL,NULL,NULL,NULL}
  23. };

    关于匿名内存映射的优点

    mmem_zero方案:

     
      
    1. (SVR4)/dev/zeroMemoryMapping

    1. 可以将伪设备 “/dev/zero” 作为参数传递给mmap而创建一个映射区。/dev/zero的特殊在于,对于该设备文件所有的读操作都返回值为0的指定长度的字节流 ,任何写入的内容都被丢弃。我们的兴趣在于用它来创建映射区,用/dev/zero创建的映射区,其内容被初始为0。

    2. 使用/dev/zero的优点在于,mmap创建映射区时,不需要一个时间存在的文件,伪文件 /dev/zero 就足够了。缺点是只能用在相关进程间。相对于相关进程间的通信,使用线程间通信效率要更高一些。不管使用那种技术,对共享数据的访问都需要进行同步。

    mmem_anon方案:

     
      
    1. (4.4BSD)AnonymousMemoryMapping

    1. 匿名内存映射与使用/dev/zero类型,都不需要真实的文件。要使用匿名映射之需要向mmap传入MAP_ANON标志,并且fd参数置为-1。

    2. 所谓匿名,指的是映射区并没有通过fd与文件路径名相关联。匿名内存映射用在有血缘关系的进程间。

    win32方案中堆内存分配的声明

    函数HeapAlloc声明如下:

     
      
    1. WINBASEAPI
    2. __out_opt
    3. HANDLE
    4. WINAPI
    5. HeapCreate(
    6. __inDWORDflOptions,
    7. __inSIZE_TdwInitialSize,
    8. __inSIZE_TdwMaximumSize
    9. );
    10. WINBASEAPI
    11. BOOL
    12. WINAPI
    13. HeapDestroy(
    14. __inHANDLEhHeap
    15. );
    16. WINBASEAPI
    17. __bcount(dwBytes)
    18. LPVOID
    19. WINAPI
    20. HeapAlloc(
    21. __inHANDLEhHeap,
    22. __inDWORDdwFlags,
    23. __inSIZE_TdwBytes
    24. );
    25. WINBASEAPI
    26. BOOL
    27. WINAPI
    28. HeapFree(
    29. __inoutHANDLEhHeap,
    30. __inDWORDdwFlags,
    31. __derefLPVOIDlpMem
    32. );
    33. WINBASEAPI
    34. SIZE_T
    35. WINAPI
    36. HeapSize(
    37. __inHANDLEhHeap,
    38. __inDWORDdwFlags,
    39. __inLPCVOIDlpMem
    40. );

    ◆hHeap是进程堆内存开始位置。

    ◆dwFlags是分配堆内存的标志。

    ◆dwBytes是分配堆内存的大小。

    初始化

    在zend_mm_startup启动时,程序会根据配置设置内存分配方案和段分配大小,如下所示代码:

     
      
    1. ZEND_APIzend_mm_heap*zend_mm_startup(void)
    2. {
    3. inti;
    4. size_tseg_size;
    5. char*mem_type=getenv("ZEND_MM_MEM_TYPE");
    6. char*tmp;
    7. constzend_mm_mem_handlers*handlers;
    8. zend_mm_heap*heap;
    9. if(mem_type==NULL){
    10. i=0;
    11. }else{
    12. for(i=0;mem_handlers[i].name;i++){
    13. if(strcmp(mem_handlers[i].name,mem_type)==0){
    14. break;
    15. }
    16. }
    17. if(!mem_handlers[i].name){
    18. fprintf(stderr,"Wrongorunsupportedzend_mmstoragetype'%s'\n",mem_type);
    19. fprintf(stderr,"supportedtypes:\n");
    20. for(i=0;mem_handlers[i].name;i++){
    21. fprintf(stderr,"'%s'\n",mem_handlers[i].name);
    22. }
    23. exit(255);
    24. }
    25. }
    26. handlers=&mem_handlers[i];
    27. tmp=getenv("ZEND_MM_SEG_SIZE");
    28. if(tmp){
    29. seg_size=zend_atoi(tmp,0);
    30. if(zend_mm_low_bit(seg_size)!=zend_mm_high_bit(seg_size)){
    31. fprintf(stderr,"ZEND_MM_SEG_SIZEmustbeapoweroftwo\n");
    32. exit(255);
    33. }elseif(seg_size<ZEND_MM_ALIGNED_SEGMENT_SIZE+ZEND_MM_ALIGNED_HEADER_SIZE){
    34. fprintf(stderr,"ZEND_MM_SEG_SIZEistoosmall\n");
    35. exit(255);
    36. }
    37. }else{
    38. seg_size=ZEND_MM_SEG_SIZE;
    39. }
    40. //....代码省略
    41. }

    第1121~1138行遍历整个mem_handlers数组,确认内存分配方案,如果没有设置ZEND_MM_MEM_TYPE变量,默认使用malloc方案,如果是windows(即ZEND_WIN32),则默认使用win32方案,如果设置了ZEND_MM_MEM_TYPE变量,则采用设置的方案。

    第1140~1152行确认段分配大小,如果设置了ZEND_MM_SEG_SIZE变量,则使用设置的大小,此处会判断所设置的大小是否满足2的倍数,并且大于或等于ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE;如果没有设置没使用默认的ZEND_MM_SEG_SIZE。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值