訪問這裏,獲取更多原創內容。函數
說明:本系列的文章基於Nginx-1.5.0版本代碼。佈局
在上一篇」基於塊的內存釋放「中,咱們已經見過一個函數:學習
static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, ngx_uint_t pages);
單從名字應該就已經可以猜到這個函數的做用了,沒錯,就是本篇的主題 --- 」基於頁的內存釋放「,當釋放的內存類型爲」NGX_SLAB_PAGE「,或者與待釋放的內存塊所對應的頁已經徹底釋放時,就到了這個函數大顯身手的時候了,但它的內容卻只有短短的十幾行代碼:ui
static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, ngx_uint_t pages) { ngx_slab_page_t *prev; page->slab = pages--; /*若是待釋放的內存空間不止一頁,則須要將後續的頁管理單元恢復爲初始化狀態*/ if (pages) { ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t)); } /*根據前面幾篇的內容能夠知道,當頁內存管理單元掛接在slot分級鏈表下時,page->next是不爲空的,這時須要先將其從鏈表中摘除*/ if (page->next) { prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK); prev->next = page->next; page->next->prev = page->prev; } /*將待釋放的空閒頁管理單元掛接到free鏈表的首部,但這裏並無作額外的合併動做*/ page->prev = (uintptr_t) &pool->free; page->next = pool->free.next; page->next->prev = (uintptr_t) page; pool->free.next = page; }
這裏咱們以第二篇「基於頁的內存分配」中的最後一幅圖所描述的場景爲例,也就是說在初始化以後相繼分配了m0頁,1頁,m1頁,1頁,而後咱們來釋放掉中間的m1頁,這時的內存佈局就變成了以下的形式:spa
這裏須要指出的是,ngx_slab_free_pages()函數只是將待釋放的內存頁管理單元從新掛接到了free鏈表的首部,而沒有嘗試進行合併,因此當程序運行一段時間後你可能會發現空閒的內存明明還有不少,但大塊的內存申請老是會返回錯誤,就是由於內存被碎片化了,沒有知足要求的連續內存段能夠分配了。這個問題在更新的版本中不知道有沒有改進。.net
除此以外還有一種狀況就是:當待釋放的內存頁是用於小塊內存分配時,調用ngx_slab_free_pages()函數時對應的頁內存管理單元還掛接在slot分級鏈表下,這時須要先將頁內存管理單元從slot分級鏈表中摘除,而後再掛接到free空閒頁鏈表中。code
若是你看到這裏了,那麼Nginx slab內存管理相關內容的學習應該也告一段落了,這個系列的文章中加入了很多內存佈局圖,目的就在於更加清楚直觀地展示出內存分配和釋放這些動態過程當中的各個細節,方便你們理解。blog
但願這個系列的文章可以對你有幫助。內存