詳解PHP內(nèi)存池中的存儲(chǔ)層
PHP的內(nèi)存管理器是分層(hierarchical)的。這個(gè)管理器共有三層:存儲(chǔ)層(storage)、堆(heap)層和 emalloc/efree 層。存儲(chǔ)層通過 malloc()、mmap() 等函數(shù)向系統(tǒng)真正的申請(qǐng)內(nèi)存,并通過free()函數(shù)釋放所申請(qǐng)的內(nèi)存。
存儲(chǔ)層通常申請(qǐng)的內(nèi)存塊都比較大,這里申請(qǐng)的內(nèi)存大并不是指storage層結(jié)構(gòu)所需要的內(nèi)存大,只是堆層通過調(diào)用存儲(chǔ)層的分配方法時(shí),其以段的格式申請(qǐng)的內(nèi)存比較大,存儲(chǔ)層的作用是將內(nèi)存分配的方式對(duì)堆層透明化。
首先看storage層的結(jié)構(gòu):
- /* Heaps with user defined storage */
- typedef struct _zend_mm_storage zend_mm_storage;
- typedef struct _zend_mm_segment {
- size_t size;
- struct _zend_mm_segment *next_segment;
- } zend_mm_segment;
- typedef struct _zend_mm_mem_handlers {
- const char *name;
- zend_mm_storage* (*init)(void *params); //初始化函數(shù)
- void (*dtor)(zend_mm_storage *storage); //析構(gòu)函數(shù)
- void (*compact)(zend_mm_storage *storage);
- zend_mm_segment* (*_alloc)(zend_mm_storage *storage, size_t size); //內(nèi)存分配函數(shù)
- zend_mm_segment* (*_realloc)(zend_mm_storage *storage, zend_mm_segment *ptr, size_t size); //重新分配內(nèi)存函數(shù)
- void (*_free)(zend_mm_storage *storage, zend_mm_segment *ptr); //釋放內(nèi)存函數(shù)
- } zend_mm_mem_handlers;
- struct _zend_mm_storage {
- const zend_mm_mem_handlers *handlers; //處理函數(shù)集
- void *data;
- };
內(nèi)存的分配方式,調(diào)用的函數(shù)是_zend_mm_storage結(jié)構(gòu)中的處理函數(shù)集,而內(nèi)存是以段的形式表現(xiàn)的。
4種內(nèi)存方案
PHP在存儲(chǔ)層共有4種內(nèi)存分配方案: malloc,win32,mmap_anon,mmap_zero。默認(rèn)使用malloc分配內(nèi)存,如果設(shè)置了ZEND_WIN32宏,則為windows版本,調(diào)用HeapAlloc分配內(nèi)存,剩下兩種內(nèi)存方案為匿名內(nèi)存映射,并且PHP的內(nèi)存方案可以通過設(shè)置變量來修改。
官方說明如下:
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.
在代碼中,對(duì)于這4種內(nèi)存分配方案,分別對(duì)應(yīng)實(shí)現(xiàn)了zend_mm_mem_handlers中的各個(gè)處理函數(shù)。配合代碼的簡(jiǎn)單說明如下:
- /* 使用mmap內(nèi)存映射函數(shù)分配內(nèi)存 寫入時(shí)拷貝的私有映射,并且匿名映射,映射區(qū)不與任何文件關(guān)聯(lián)。*/
- # define ZEND_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}
- /* 使用mmap內(nèi)存映射函數(shù)分配內(nèi)存 寫入時(shí)拷貝的私有映射,并且映射到/dev/zero。*/
- # define ZEND_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}
- /* 使用HeapAlloc分配內(nèi)存 windows版本 關(guān)于這點(diǎn),注釋中寫的是VirtualAlloc() to allocate memory,實(shí)際在程序中使用的是HeapAlloc*/
- # define ZEND_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}
- /* 使用malloc分配內(nèi)存 默認(rèn)為此種分配 如果有加ZEND_WIN32宏,則使用win32的分配方案*/
- # define ZEND_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}
- static const zend_mm_mem_handlers mem_handlers[] = {
- #ifdef HAVE_MEM_WIN32
- ZEND_MM_MEM_WIN32_DSC,
- #endif
- #ifdef HAVE_MEM_MALLOC
- ZEND_MM_MEM_MALLOC_DSC,
- #endif
- #ifdef HAVE_MEM_MMAP_ANON
- ZEND_MM_MEM_MMAP_ANON_DSC,
- #endif
- #ifdef HAVE_MEM_MMAP_ZERO
- ZEND_MM_MEM_MMAP_ZERO_DSC,
- #endif
- {NULL, NULL, NULL, NULL, NULL, NULL}
- };
#p#
關(guān)于匿名內(nèi)存映射的優(yōu)點(diǎn)
mmem_zero方案:
- (SVR 4 ) /dev/zero Memory Mapping
1. 可以將偽設(shè)備 “/dev/zero” 作為參數(shù)傳遞給mmap而創(chuàng)建一個(gè)映射區(qū)。/dev/zero的特殊在于,對(duì)于該設(shè)備文件所有的讀操作都返回值為0的指定長(zhǎng)度的字節(jié)流 ,任何寫入的內(nèi)容都被丟棄。我們的興趣在于用它來創(chuàng)建映射區(qū),用/dev/zero創(chuàng)建的映射區(qū),其內(nèi)容被初始為0。
2. 使用/dev/zero的優(yōu)點(diǎn)在于,mmap創(chuàng)建映射區(qū)時(shí),不需要一個(gè)時(shí)間存在的文件,偽文件 /dev/zero 就足夠了。缺點(diǎn)是只能用在相關(guān)進(jìn)程間。相對(duì)于相關(guān)進(jìn)程間的通信,使用線程間通信效率要更高一些。不管使用那種技術(shù),對(duì)共享數(shù)據(jù)的訪問都需要進(jìn)行同步。
mmem_anon方案:
- (4.4 BSD) Anonymous Memory Mapping
1. 匿名內(nèi)存映射與使用/dev/zero類型,都不需要真實(shí)的文件。要使用匿名映射之需要向mmap傳入MAP_ANON標(biāo)志,并且fd參數(shù)置為-1。
2. 所謂匿名,指的是映射區(qū)并沒有通過fd與文件路徑名相關(guān)聯(lián)。匿名內(nèi)存映射用在有血緣關(guān)系的進(jìn)程間。
win32方案中堆內(nèi)存分配的聲明
函數(shù)HeapAlloc聲明如下:
- WINBASEAPI
- __out_opt
- HANDLE
- WINAPI
- HeapCreate(
- __in DWORD flOptions,
- __in SIZE_T dwInitialSize,
- __in SIZE_T dwMaximumSize
- );
- WINBASEAPI
- BOOL
- WINAPI
- HeapDestroy(
- __in HANDLE hHeap
- );
- WINBASEAPI
- __bcount(dwBytes)
- LPVOID
- WINAPI
- HeapAlloc(
- __in HANDLE hHeap,
- __in DWORD dwFlags,
- __in SIZE_T dwBytes
- );
- WINBASEAPI
- BOOL
- WINAPI
- HeapFree(
- __inout HANDLE hHeap,
- __in DWORD dwFlags,
- __deref LPVOID lpMem
- );
- WINBASEAPI
- SIZE_T
- WINAPI
- HeapSize(
- __in HANDLE hHeap,
- __in DWORD dwFlags,
- __in LPCVOID lpMem
- );
◆hHeap是進(jìn)程堆內(nèi)存開始位置。
◆dwFlags是分配堆內(nèi)存的標(biāo)志。
◆dwBytes是分配堆內(nèi)存的大小。
#p#
初始化
在zend_mm_startup啟動(dòng)時(shí),程序會(huì)根據(jù)配置設(shè)置內(nèi)存分配方案和段分配大小,如下所示代碼:
- ZEND_API zend_mm_heap *zend_mm_startup(void)
- {
- int i;
- size_t seg_size;
- char *mem_type = getenv("ZEND_MM_MEM_TYPE");
- char *tmp;
- const zend_mm_mem_handlers *handlers;
- zend_mm_heap *heap;
- if (mem_type == NULL) {
- i = 0;
- } else {
- for (i = 0; mem_handlers[i].name; i++) {
- if (strcmp(mem_handlers[i].name, mem_type) == 0) {
- break;
- }
- }
- if (!mem_handlers[i].name) {
- fprintf(stderr, "Wrong or unsupported zend_mm storage type '%s'\n", mem_type);
- fprintf(stderr, " supported types:\n");
- for (i = 0; mem_handlers[i].name; i++) {
- fprintf(stderr, " '%s'\n", mem_handlers[i].name);
- }
- exit(255);
- }
- }
- handlers = &mem_handlers[i];
- tmp = getenv("ZEND_MM_SEG_SIZE");
- if (tmp) {
- seg_size = zend_atoi(tmp, 0);
- if (zend_mm_low_bit(seg_size) != zend_mm_high_bit(seg_size)) {
- fprintf(stderr, "ZEND_MM_SEG_SIZE must be a power of two\n");
- exit(255);
- } else if (seg_size < ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE) {
- fprintf(stderr, "ZEND_MM_SEG_SIZE is too small\n");
- exit(255);
- }
- } else {
- seg_size = ZEND_MM_SEG_SIZE;
- }
- //....代碼省略
- }
第1121~1138行遍歷整個(gè)mem_handlers數(shù)組,確認(rèn)內(nèi)存分配方案,如果沒有設(shè)置ZEND_MM_MEM_TYPE變量,默認(rèn)使用malloc方案,如果是windows(即ZEND_WIN32),則默認(rèn)使用win32方案,如果設(shè)置了ZEND_MM_MEM_TYPE變量,則采用設(shè)置的方案。
第1140~1152行確認(rèn)段分配大小,如果設(shè)置了ZEND_MM_SEG_SIZE變量,則使用設(shè)置的大小,此處會(huì)判斷所設(shè)置的大小是否滿足2的倍數(shù),并且大于或等于ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE;如果沒有設(shè)置沒使用默認(rèn)的ZEND_MM_SEG_SIZE。
原文鏈接:http://www.phppan.com/2010/11/php-source-code-30-memory-pool-storage/
【編輯推薦】