baiyanphp
所有視頻:https://segmentfault.com/a/11...segmentfault
源視頻地址:http://replay.xesv5.com/ll/24...數組
答:在small和large內存分配規格中,首先會分配一個chunk,chunk的第一個page中存有一個結構體,用來保存chunk中每個page的相關存儲信息(即zend_alloc.h中的mm_heap結構體)。可是huge規格不須要,直接拿來用便可(由於huge內存已經大於2MB(即一個chunk)),無需記錄內部存儲細節信息)。思考一個問題:一個page有4KB,至於用這麼大的空間去存嗎?安全
struct _zend_mm_heap { #if ZEND_MM_CUSTOM int use_custom_heap; #endif #if ZEND_MM_STORAGE zend_mm_storage *storage; #endif #if ZEND_MM_STAT size_t size; /* 當前使用內存(PHP庫函數memory_get_usage()方法就是取此字段的值)*/ size_t peak; /* 內存使用峯值 */ #endif zend_mm_free_slot *free_slot[ZEND_MM_BINS]; /*它是一個大小爲ZEND_MM_BINS = 30的數組,每一個存儲單元是一個單向鏈表,用來掛載30種不一樣規格的small內存(下面會講)*/ #if ZEND_MM_STAT || ZEND_MM_LIMIT size_t real_size; /* current size of allocated pages */ #endif #if ZEND_MM_STAT size_t real_peak; /* peak size of allocated pages */ #endif #if ZEND_MM_LIMIT size_t limit; /* memory limit */ int overflow; /* memory overflow flag */ #endif zend_mm_huge_list *huge_list; /* list of huge allocated blocks */ zend_mm_chunk *main_chunk; zend_mm_chunk *cached_chunks; /* list of unused chunks */ int chunks_count; /* number of alocated chunks */ int peak_chunks_count; /* peak number of allocated chunks for current request */ int cached_chunks_count; /* number of cached chunks */ double avg_chunks_count; /* average number of chunks allocated per request */ int last_chunks_delete_boundary; /* numer of chunks after last deletion */ int last_chunks_delete_count; /* number of deletion over the last boundary */ #if ZEND_MM_CUSTOM union { struct { void *(*_malloc)(size_t); void (*_free)(void*); void *(*_realloc)(void*, size_t); } std; struct { void *(*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); void *(*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); } debug; } custom_heap; #endif };
struct _zend_mm_chunk { zend_mm_heap *heap; zend_mm_chunk *next; zend_mm_chunk *prev; uint32_t free_pages; /* number of free pages */ uint32_t free_tail; /* number of free pages at the end of chunk */ uint32_t num; char reserve[64 - (sizeof(void*) * 3 + sizeof(uint32_t) * 3)]; zend_mm_heap heap_slot; /* used only in main chunk */ zend_mm_page_map free_map; /* 512 bits or 64 bytes */ zend_mm_page_info map[ZEND_MM_PAGES]; /* 2 KB = 512 * 4 */ };
#define ZEND_MM_IS_FRUN 0x00000000 #define ZEND_MM_IS_LRUN 0x40000000 #define ZEND_MM_IS_SRUN 0x80000000
static zend_always_inline void *zend_mm_alloc_heap(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { void *ptr; if (size <= ZEND_MM_MAX_SMALL_SIZE) { ptr = zend_mm_alloc_small(heap, size, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); return ptr; } else if (size <= ZEND_MM_MAX_LARGE_SIZE) { ptr = zend_mm_alloc_large(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); return ptr; } else { return zend_mm_alloc_huge(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } }
例3:假如分配40B規格的small內存,爲何下面代碼也僅僅用了1個page,那是由於4KB和40B的最小公倍數過大,致使一次性申請的page太多,因此不能一味的找兩者的最小公倍數,因此退而求其次,只分配1個page。雖然4096/40 = 102.5,代碼中直接取了102做爲最終分配的數量。第1個返回給用戶,剩下101個掛在free_slot鏈表上等待後續的分配請求。函數
/* bin_num, size, count, pages */ /*數組下標,分配規格大小,共能分配多少個這樣規格大小的內存塊,須要的頁的數量*/ #define ZEND_MM_BINS_INFO(_, x, y) \ _( 0, 8, 512, 1, x, y) \ _( 1, 16, 256, 1, x, y) \ _( 2, 24, 170, 1, x, y) \ _( 3, 32, 128, 1, x, y) \ _( 4, 40, 102, 1, x, y) \ _( 5, 48, 85, 1, x, y) \ _( 6, 56, 73, 1, x, y) \ _( 7, 64, 64, 1, x, y) \ _( 8, 80, 51, 1, x, y) \ _( 9, 96, 42, 1, x, y) \ _(10, 112, 36, 1, x, y) \ _(11, 128, 32, 1, x, y) \ _(12, 160, 25, 1, x, y) \ _(13, 192, 21, 1, x, y) \ _(14, 224, 18, 1, x, y) \ _(15, 256, 16, 1, x, y) \ _(16, 320, 64, 5, x, y) \ _(17, 384, 32, 3, x, y) \ _(18, 448, 9, 1, x, y) \ _(19, 512, 8, 1, x, y) \ _(20, 640, 32, 5, x, y) \ _(21, 768, 16, 3, x, y) \ _(22, 896, 9, 2, x, y) \ _(23, 1024, 8, 2, x, y) \ _(24, 1280, 16, 5, x, y) \ _(25, 1536, 8, 3, x, y) \ _(26, 1792, 16, 7, x, y) \ _(27, 2048, 8, 4, x, y) \ _(28, 2560, 8, 5, x, y) \ _(29, 3072, 4, 3, x, y)
static zend_always_inline int zend_mm_small_size_to_bin(size_t size) { ... unsigned int t1, t2; if (size <= 64) { /* we need to support size == 0 ... */ return (size - !!size) >> 3; } else { t1 = size - 1; t2 = zend_mm_small_size_to_bit(t1) - 3; t1 = t1 >> t2; t2 = t2 - 3; t2 = t2 << 2; return (int)(t1 + t2); } #endif }
static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, size_t size, int bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { ... if (EXPECTED(heap->free_slot[bin_num] != NULL)) { zend_mm_free_slot *p = heap->free_slot[bin_num]; heap->free_slot[bin_num] = p->next_free_slot; return (void*)p; } else { return zend_mm_alloc_small_slow(heap, bin_num ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } }