新聞中心
PHP的內(nèi)存管理器是分層(hierarchical)的。這個管理器共有三層:存儲層(storage)、堆(heap)層和 emalloc/efree 層。存儲層通過 malloc()、mmap() 等函數(shù)向系統(tǒng)真正的申請內(nèi)存,并通過free()函數(shù)釋放所申請的內(nèi)存。

創(chuàng)新互聯(lián)建站專注于定海企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,成都做商城網(wǎng)站。定海網(wǎng)站建設(shè)公司,為定海等地區(qū)提供建站服務(wù)。全流程按需網(wǎng)站制作,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)建站專業(yè)和態(tài)度為您提供的服務(wù)
存儲層通常申請的內(nèi)存塊都比較大,這里申請的內(nèi)存大并不是指storage層結(jié)構(gòu)所需要的內(nèi)存大,只是堆層通過調(diào)用存儲層的分配方法時,其以段的格式申請的內(nèi)存比較大,存儲層的作用是將內(nèi)存分配的方式對堆層透明化。
首先看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在存儲層共有4種內(nèi)存分配方案: malloc,win32,mmap_anon,mmap_zero。默認使用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.
在代碼中,對于這4種內(nèi)存分配方案,分別對應(yīng)實現(xiàn)了zend_mm_mem_handlers中的各個處理函數(shù)。配合代碼的簡單說明如下:
- /* 使用mmap內(nèi)存映射函數(shù)分配內(nèi)存 寫入時拷貝的私有映射,并且匿名映射,映射區(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)存 寫入時拷貝的私有映射,并且映射到/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)于這點,注釋中寫的是VirtualAlloc() to allocate memory,實際在程序中使用的是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)存 默認為此種分配 如果有加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)點
mmem_zero方案:
- (SVR 4 ) /dev/zero Memory Mapping
1. 可以將偽設(shè)備 “/dev/zero” 作為參數(shù)傳遞給mmap而創(chuàng)建一個映射區(qū)。/dev/zero的特殊在于,對于該設(shè)備文件所有的讀操作都返回值為0的指定長度的字節(jié)流 ,任何寫入的內(nèi)容都被丟棄。我們的興趣在于用它來創(chuàng)建映射區(qū),用/dev/zero創(chuàng)建的映射區(qū),其內(nèi)容被初始為0。
2. 使用/dev/zero的優(yōu)點在于,mmap創(chuàng)建映射區(qū)時,不需要一個時間存在的文件,偽文件 /dev/zero 就足夠了。缺點是只能用在相關(guān)進程間。相對于相關(guān)進程間的通信,使用線程間通信效率要更高一些。不管使用那種技術(shù),對共享數(shù)據(jù)的訪問都需要進行同步。
mmem_anon方案:
- (4.4 BSD) Anonymous Memory Mapping
1. 匿名內(nèi)存映射與使用/dev/zero類型,都不需要真實的文件。要使用匿名映射之需要向mmap傳入MAP_ANON標志,并且fd參數(shù)置為-1。
2. 所謂匿名,指的是映射區(qū)并沒有通過fd與文件路徑名相關(guān)聯(lián)。匿名內(nèi)存映射用在有血緣關(guā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是進程堆內(nèi)存開始位置。
◆dwFlags是分配堆內(nèi)存的標志。
◆dwBytes是分配堆內(nèi)存的大小。
#p#
初始化
在zend_mm_startup啟動時,程序會根據(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行遍歷整個mem_handlers數(shù)組,確認內(nèi)存分配方案,如果沒有設(shè)置ZEND_MM_MEM_TYPE變量,默認使用malloc方案,如果是windows(即ZEND_WIN32),則默認使用win32方案,如果設(shè)置了ZEND_MM_MEM_TYPE變量,則采用設(shè)置的方案。
第1140~1152行確認段分配大小,如果設(shè)置了ZEND_MM_SEG_SIZE變量,則使用設(shè)置的大小,此處會判斷所設(shè)置的大小是否滿足2的倍數(shù),并且大于或等于ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE;如果沒有設(shè)置沒使用默認的ZEND_MM_SEG_SIZE。
原文鏈接:http://www.phppan.com/2010/11/php-source-code-30-memory-pool-storage/
【編輯推薦】
- PHP程序設(shè)計最佳實踐
- 10位頂級PHP大師的開發(fā)原則
- 玩轉(zhuǎn)PHP關(guān)聯(lián)數(shù)組的10個技巧
- 詳解PHP對構(gòu)造方法的識別
- PHP企業(yè)級應(yīng)用緩存技術(shù)詳解
分享標題:詳解PHP內(nèi)存池中的存儲層
分享地址:http://m.fisionsoft.com.cn/article/coojijg.html


咨詢
建站咨詢
