色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

glibc的內存分配回收策略

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-13 11:16 ? 次閱讀

Linux內存空間簡介

32位Linux平臺下進程虛擬地址空間分布如下圖:

圖片

進程虛擬地址空間分布

圖中,0xC0000000開始的最高1G空間是內核地址空間,剩下3G空間是用戶態空間。用戶態空間從上到下依次為stack棧(向下增長)、mmap(匿名文件映射區)、Heap堆(向上增長)、bss數據段、數據段、只讀代碼段。

其中,Heap區是程序的動態內存區,同時也是C++內存泄漏的溫床。malloc、free均發生在這個區域。本文將簡單介紹下glibc在動態內存管理方面的機制,拋磚引玉,希望能和大家多多交流。

Linux提供了如下幾個系統調用,用于內存分配:

brk()/sbrk() // 通過移動Heap堆頂指針brk,達到增加內存目的 
mmap()/munmap() // 通過文件影射的方式,把文件映射到mmap區

這兩種方式分配的都是虛擬內存,沒有分配物理內存。在第一次訪問已分配的虛擬地址空間的時候,發生缺頁中斷,操作系統負責分配物理內存,然后建立虛擬內存和物理內存之間的映射關系。

那么,既然brk、mmap提供了內存分配的功能,直接使用brk、mmap進行內存管理不是更簡單嗎,為什么需要glibc呢?我們知道,系統調用本身會產生軟中斷,導致程序從用戶態陷入內核態,比較消耗資源。試想,如果頻繁分配回收小塊內存區,那么將有很大的性能耗費在系統調用中。因此,為了減少系統調用帶來的性能損耗,glibc采用了內存池的設計,增加了一個代理層,每次內存分配,都優先從內存池中尋找,如果內存池中無法提供,再向操作系統申請。

一切計算機的問題都可以通過加層的方式解決。

glibc的內存分配回收策略

glibc中malloc內存分配邏輯如下是:

圖片

malloc

  • 分配內存 < DEFAULT_MMAP_THRESHOLD,走__brk,從內存池獲取,失敗的話走brk系統調用
  • 分配內存 > DEFAULT_MMAP_THRESHOLD,走__mmap,直接調用mmap系統調用

其中,DEFAULT_MMAP_THRESHOLD默認為128k,可通過mallopt進行設置。重點看下小塊內存(size > DEFAULT_MMAP_THRESHOLD)的分配,glibc使用的內存池如下圖示:

圖片

內存池

內存池保存在bins這個長128的數組中,每個元素都是一雙向個鏈表。其中:

  • bins[0]目前沒有使用
  • bins[1]的鏈表稱為unsorted_list,用于維護free釋放的chunk。
  • bins[2,63)的區間稱為small_bins,用于維護<512字節的內存塊,其中每個元素對應的鏈表中的chunk大小相同,均為index*8。
  • bins[64,127)稱為large_bins,用于維護>512字節的內存塊,每個元素對應的鏈表中的chunk大小不同,index越大,鏈表中chunk的內存大小相差越大,例如: 下標為64的chunk大小介于[512, 512+64),下標為95的chunk大小介于[2k+1,2k+512)。同一條鏈表上的chunk,按照從小到大的順序排列。

chunk數據結構

圖片

chunk結構

glibc在內存池中查找合適的chunk時,采用了最佳適應的伙伴算法。舉例如下:

1、如果分配內存<512字節,則通過內存大小定位到smallbins對應的index上(floor(size/8))

  • 如果smallbins[index]為空,進入步驟3
  • 如果smallbins[index]非空,直接返回第一個chunk

2、如果分配內存>512字節,則定位到largebins對應的index上

  • 如果largebins[index]為空,進入步驟3
  • 如果largebins[index]非空,掃描鏈表,找到第一個大小最合適的chunk,如size=12.5K,則使用chunk B,剩下的0.5k放入unsorted_list中

3、遍歷unsorted_list,查找合適size的chunk,如果找到則返回;否則,將這些chunk都歸類放到smallbins和largebins里面

4、index++從更大的鏈表中查找,直到找到合適大小的chunk為止,找到后將chunk拆分,并將剩余的加入到unsorted_list中

5、如果還沒有找到,那么使用top chunk

6、或者,內存<128k,使用brk;內存>128k,使用mmap獲取新內存

top chunk 如下圖示: top chunk是堆頂的chunk,堆頂指針brk位于top chunk的頂部。移動brk指針,即可擴充top chunk的大小。當top chunk大小超過128k(可配置)時,會觸發malloc_trim操作,調用sbrk(-size)將內存歸還操作系統。

圖片

chunk分布圖

free釋放內存時,有兩種情況:

  1. chunk和top chunk相鄰,則和top chunk合并
  2. chunk和top chunk不相鄰,則直接插入到unsorted_list中

內存碎片

以上圖chunk分布圖為例,按照glibc的內存分配策略,我們考慮下如下場景(假設brk其實地址是512k):

  1. malloc 40k內存,即chunkA,brk = 512k + 40k = 552k
  2. malloc 50k內存,即chunkB,brk = 552k + 50k = 602k
  3. malloc 60k內存,即chunkC,brk = 602k + 60k = 662k
  4. free chunkA。

此時,由于brk = 662k,而釋放的內存是位于[512k, 552k]之間,無法通過移動brk指針,將區域內內存交還操作系統,因此,在[512k, 552k]的區域內便形成了一個內存空洞 ---- 內存碎片。按照glibc的策略,free后的chunkA區域由于不和top chunk相鄰,因此,無法和top chunk 合并,應該掛在unsorted_list鏈表上。

glibc實現的一些重要結構

glibc中用于維護空閑內存的結構體是malloc_state,其主要定義如下:

struct malloc_state {
    mutex_t mutex; // 并發編程下鎖的競爭
    mchunkptr        top; // top chunk
    unsigned int     binmap[BINMAPSIZE]; // bitmap,加快bins中chunk判定
    mchunkptr        bins[NBINS * 2 - 2]; // bins,上文所述
    mfastbinptr      fastbinsY[NFASTBINS]; // fastbins,類似bins,維護的chunk更小(80字節的chunk鏈表)
...
}
static struct malloc_state main_arena; // 主arena

多線程下的競爭搶鎖

并發條件下,main_arena引發的競爭將會成為限制程序性能的瓶頸所在,因此glibc采用了多arena機制,線程A分配內存時獲取main_arena鎖成功,將在main_arena所管理的內存中分配;此時線程B獲取main_arena失敗,glibc會新建一個arena1,此次內存分配從arena1中進行。這種策略,一定程度上解決了多線程下競爭的問題;但是隨著arena的增多,內存碎片出現的可能性也變大了。例如,main_arena中有10k、20k的空閑內存,線程B要獲取20k的空閑內存,但是獲取main_arena鎖失敗,導致留下20k的碎片,降低了內存使用率。

普通arena的內部結構:

圖片

普通arena結構

  1. 一個arena由多個Heap構成
  2. 每個Heap通過mmap獲得,最大為1M,多個Heap間可能不相鄰
  3. Heap之間有prev指針指向前一個Heap
  4. 最上面的Heap,也有top chunk

每個Heap里面也是由chunk組成,使用和main_arena完全相同的管理方式管理空閑chunk。多個arena之間是通過鏈表連接的。如下圖:

圖片

arena鏈表

main arena和普通arena的區別 main_arena是為一個使用brk指針的arena,由于brk是堆頂指針,一個進程中只可能有一個,因此普通arena無法使用brk進行內存分配。普通arena建立在mmap的機制上,內存管理方式和main_arena類似,只有一點區別,普通arena只有在整個arena都空閑時,才會調用munmap把內存還給操作系統。

一些特殊情況的分析

根據上文所述,glibc在調用malloc_trim時,需要滿足如下2個條件:

1. size(top chunk) > 128K
2. brk = top chunk- >base + size(top chunk)

假設,brk指針上面的空間已經被占用,無法通過移動brk指針獲得新的地址空間,此時main_arena就無法擴容了嗎?glibc的設計考慮了這樣的特殊情況,此時,glibc會換用mmap操作來獲取新空間(每次最少MMAP_AS_MORECORE_SIZE<1M>)。這樣,main_arena和普通arena一樣,由非連續的Heap塊構成,不過這種情況下,glibc并未將這種mmap空間表示為Heap,因此,main_arena多個塊之間是沒有聯系的,這就導致了main_arena從此無法歸還給操作系統,永遠保留在空閑內存中了。如下圖示:

圖片

main_arena無法回收

顯而易見,此時根本不可能滿足調用malloc_trim的條件2,即:brk !== top chunk->base + size(top chunk),因為此時brk處于堆頂,而top chunk->base > brk.

#include < stdlib.h >
#include < stdio.h >
#include < string.h >
#include < unistd.h >
#include < sys/mman.h >
#include < malloc.h >

#define ARRAY_SIZE 127
char cmd[1024];

void print_info()
{
    struct mallinfo mi = mallinfo();           
    system(cmd);
    printf("theap_malloc_total=%lu heap_free_total=%lu heap_in_use=%lun
            tmmap_total=%lu mmap_count=%lun", mi.arena, mi.fordblks, mi.uordblks, mi.hblkhd, mi.hblks);
}

int main(int argc, char** argv)
{
    char** ptr_arr[ARRAY_SIZE];
    int i;
    char*  mmap_var;
    pid_t  pid;
    pid = getpid();
    sprintf(cmd, "ps aux | grep %lu | grep -v grep", pid);
    /* mmap占據堆頂后1M的地址空間 */
    mmap_var = mmap((void*)sbrk(0) + 1024*1024, 127*1024, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 
    printf("before mallocn");
    print_info();

    /* 分配內存,總大小超過1M,導致main_arena被拆分 */
    for( i = 0; i < ARRAY_SIZE; i++) {
        ptr_arr[i] = malloc(i * 1024);
    }   
    printf("nafter mallocn");
    print_info();
    /* 釋放所有內存,觀察內存使用是否改變 */
    for( i = 0; i < ARRAY_SIZE; i++) {
        free(ptr_arr[i]);
    }
    printf("nafter freen");
    print_info();
    munmap(mmap_var, 127*1024);
    return 1;
}

圖片

異常運行

作為對比,去除掉brk上面的mmap區再次運行后結果如下:

圖片

正常運行

可以看出,異常情況下(brk無法擴展),free的內存沒有歸還操作系統,而是留在了main_arena的unsorted_list了;而正常情況下,由于滿足執行malloc_trim的條件,因此,free后,調用了sbrk(-size)把內存歸還了操作系統,main_arena內存響應減少。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 數據
    +關注

    關注

    8

    文章

    7004

    瀏覽量

    88944
  • 內存
    +關注

    關注

    8

    文章

    3019

    瀏覽量

    74008
  • 程序
    +關注

    關注

    117

    文章

    3785

    瀏覽量

    81005
  • C++
    C++
    +關注

    關注

    22

    文章

    2108

    瀏覽量

    73623
  • Glibc
    +關注

    關注

    0

    文章

    9

    瀏覽量

    7500
收藏 人收藏

    評論

    相關推薦

    C語言知識總結:動態內存分配

    動態內存分配就 是指在程序執行的過程中動態地分配或者回收存儲空間的分配內存的方法。動態
    發表于 10-24 15:52 ?856次閱讀

    C語言既然可以自動為變量分配內存,為什么還要用動態分配內存呢?

    不知道大家在學習C語言動態分配內存的時候有沒有過這樣的疑問,既然系統可以自動幫我們分配內存,為什么還需要我們程序員自己去分配
    發表于 12-13 11:14 ?1047次閱讀

    glibc malloc內存分配器的實現原理

    內存(Heap Memory)是一個很有意思的領域。你可能和我一樣,也困惑于下述問題很久了。
    的頭像 發表于 01-17 10:03 ?812次閱讀
    <b class='flag-5'>glibc</b> malloc<b class='flag-5'>內存</b><b class='flag-5'>分配</b>器的實現原理

    Linux內存系統: Linux 內存分配算法

    、伙伴系統算法——組織結構1) 概念· 為內核提供了一種用于分配一組連續的頁而建立的一種高效的分配策略,并有效的解決了外碎片問題· 分配內存
    發表于 08-24 07:44

    動態內存分配是什么意思

    所謂動態內存分配(Dynamic Memory Allocation)就是指在程序執行的過程中動態地分配或者回收存儲空間的分配
    發表于 12-17 08:17

    淺析cache控制器的分配策略與替換策略

    在cache的相關操作中,cache控制器需要根據需求做出許多不同的選擇。例如:分配策略是否需要將數據從主存中分配到cache中;替換策略組相聯cache中,所有的way都已經有填充數
    發表于 06-15 16:24

    一種基于Buddy算法思想、高可靠性的內存管理策略

    內存管理是操作系統的中心任務之一,其主要任務是組織內存以容納內核和待執行程序,跟蹤當前內存的使用情況,在需要時為進程分配內存,使用完畢后釋放
    發表于 11-30 16:34 ?1675次閱讀
    一種基于Buddy算法思想、高可靠性的<b class='flag-5'>內存</b>管理<b class='flag-5'>策略</b>

    進程虛擬內存布局以及進程的虛擬內存分配釋放流程,涉及的代碼

    我們計劃通過一系列文章來介紹虛擬內存分配/釋放,缺頁處理,內存壓縮/回收內存分配器等知識,梳理
    的頭像 發表于 06-28 09:38 ?4110次閱讀

    glibc內存管理存在的共性問題及解決方法

    glibc內存分配原理、內存站崗問題形成原因展開討論,并對glibc緩存大量內存(高達幾十個 G
    的頭像 發表于 06-18 14:50 ?3234次閱讀

    什么是堆內存?堆內存是如何分配的?

    在一般的編譯系統中,堆內存分配方向和棧內存是相反的。當棧內存從高地址向低地址增長的時候,堆內存從低地址向高地址
    的頭像 發表于 07-05 17:58 ?9975次閱讀

    Glibc內存管理之Ptmalloc2源代碼分析

    Glibc內存管理之Ptmalloc2源代碼分析
    發表于 07-29 09:20 ?24次下載

    Linux內存分配管理與內存回收基本框架

    內存對計算機系統來說是一項非常重要的資源,直接影響著系統運行的性能。最初的時候,系統是直接運行在物理內存上的,這存在著很多的問題,尤其是安全問題。后來出現了虛擬內存,內核和進程都運行在虛擬內存
    的頭像 發表于 06-01 16:02 ?2466次閱讀

    jemalloc分配機制的介紹及其優化實踐

    C/C++通過libc做內存分配glibc中默認的分配機制是ptmalloc。除此之外,還有眾多的不同側重的優化,例如tcmalloc,jemalloc。
    的頭像 發表于 05-30 09:12 ?1165次閱讀
    jemalloc<b class='flag-5'>分配</b>機制的介紹及其優化實踐

    glibc導致的堆外內存泄露的排查過程

    本文記錄一次glibc導致的堆外內存泄露的排查過程。
    的頭像 發表于 09-01 09:43 ?716次閱讀
    <b class='flag-5'>glibc</b>導致的堆外<b class='flag-5'>內存</b>泄露的排查過程

    轉載 golang內存分配

    . 線程擁有一定的 cache, 可用于無鎖分配. 同時 Go 對于 GC 后回收內存頁, 并不是馬上歸還給操作系統, 而是會延遲歸還, 用于滿足未來的內存需求. ?? ? 在 1.
    的頭像 發表于 09-05 14:12 ?257次閱讀
    轉載 golang<b class='flag-5'>內存</b><b class='flag-5'>分配</b>
    主站蜘蛛池模板: 亚洲 日韩 在线 国产 精品| 久久久久九九| 草草久久久无码国产专区全集观看| 野草视频在线观看| 亚洲AV无码一区二区三区乱子伦| 天天摸夜添狠狠添高| 欧洲美女高清一级毛片| 美女被爆插| 毛片免费观看| 色欲无码国产喷水AV精品| 色戒未删减版在线观看完整| 三叶草未满十八岁| 一本之道高清www在线观看| 把腿张开再深点好爽宝贝| 福利视频一二三在线观看| 成人毛片免费在线观看| 国产69精品久久久久人妻刘玥| 福利社的阿姨| 久久香蕉国产线看观看精品| 美女露出逼| 欧美一道本一区二区三区| 亚洲国产精品线在线观看| 亚洲伊人色| 777米奇色狠狠俺去啦| 69日本xxⅹxxxxx18| 国产乱人偷精品视频A人人澡| 国产精品第1页在线观看| 国产探花在线精品一区二区| 国产性色AV内射白浆肛交后入| 嫩草影院久久国产精品| 肉动漫无码无删减在线观看| 在线视频 国产 日韩 欧美| 99久久99久久久精品久久| hdxxxx58丝袜连裤袜| 二级片免费看| 免费完整版观看| 色噜噜2017最新综合| 2020亚洲 欧美 国产 日韩 | 99精品视频在线| 黄色aa大片| 免费高清毛片|