STL-內存的配置與釋放

STL-內存的配置與釋放

STL有兩級空間配置器,默認是使用第二級。第二級空間配置器會在某些情況下去調用第一級空間配置器。空間配置器都是在allocate函數內分配內存,在deallocate函數內釋放內存。

第一級空間配置器

第一級配置器只是對malloc函數和free函數的簡單封裝,在allocate內調用malloc,在deallocate內調用free。同時第一級配置器的oom_malloc函數,用來處理malloc失敗的情況。如下所示:

allocate對malloc函數簡單封裝 :

static void *allocate(size_t n)
{void *result = malloc(n);if (NULL == result)result = oom_malloc(n);return result;
}

deallocate對free函數簡單封裝 :

static void deallocate(void *p, size_t) { free(p); }

oom_malloc調用外部提供的malloc失敗處理函數,然后重新試著再次調用malloc。重復執行此過程,直到malloc成功為止 :

template <int inst>
void* __malloc_alloc<inst>::oom_malloc(size_t n)
{void (*my_malloc_handler)();void *result;for (;;){my_malloc_handler = malloc_alloc_oom_handler;if (NULL == my_malloc_handler)__THROW_BAD_ALLOC;(*my_malloc_handler)();result = malloc(n);if (result)return result;}
}

第二級空間配置器

第一級配置器直接調用malloc和free帶來了幾個問題:

  1. 內存分配/釋放的效率低。

  2. 當配置大量的小內存塊時,會導致內存碎片比較嚴重。

  3. 配置內存時,需要額外的部分空間存儲內存塊信息,所以配置大量的小內存塊時,還會導致額外內存負擔。

第二級配置器維護了一個自由鏈表數組,每次需要分配內存時,直接從相應的鏈表上取出一個內存節點就完成工作,效率很高。

自由鏈表數組

自由鏈表數組其實就是個指針數組,數組中的每個指針元素指向一個鏈表的起始節點。數組大小為16,即維護了16個鏈表,鏈表的每個節點就是實際的內存塊,相同鏈表上的內存塊大小都相同,不同鏈表的內存塊大小不同,從8一直到128。如下所示,obj為鏈表上的節點,free_list就是鏈表數組。

//自由鏈表
union obj
{union obj *free_list_link;char data[1];
};
//自由鏈表數組
static obj *volatile free_list[__NFREELISTS];

內存分配

allocate函數內先判斷要分配的內存大小,若大于128字節,直接調用第一級配置器,否則根據要分配的內存大小從16個鏈表中選出一個鏈表,取出該鏈表的第一個節點。若相應的鏈表為空,則調用refill函數填充該鏈表。如下:

template <bool threads>
void *__default_alloc<threads>::allocate(size_t n)
{obj *volatile *my_free_list;obj *result;if (n > (size_t)__MAX_BYTES) //調用第一級配置器return malloc_alloc::allocate(n);my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;if (result == NULL){//第n號鏈表無內存塊,則準備重新填充該鏈表void *r = refill(ROUND_UP(n));return r;}*my_free_list = result->free_list_link;return result;
}

填充鏈表

若allocate函數內要取出節點的鏈表為空,則會調用refill函數填充該鏈表。

refill函數內會先調用chunk_alloc函數從內存池分配一大塊內存,該內存大小默認為20個鏈表節點大小,當內存池的內存也不足時,返回的內存塊節點數目會不足20個。接著refill的工作就是將這一大塊內存分成20份相同大小的內存塊,并將各內存塊連接起來形成一個鏈表。如下:

template <bool threads>
void *__default_alloc<threads>::refill(size_t n)
{int nobjs = __NOBJS;char *chunk = chunk_alloc(n, nobjs);  //從內存池獲取內存if (nobjs == 1)  //只能分配一塊,則直接返回給調用者return chunk;obj *volatile *my_free_list;obj *result, *next_obj, *current_obj;result = (obj *)chunk;my_free_list = free_list + FREELIST_INDEX(n);*my_free_list = next_obj = (obj *)(chunk + n);for (int i = 1; i < nobjs - 1; i++)  //將剩下的區塊添加進鏈表{current_obj = next_obj;next_obj = (obj *)(char *)(next_obj + n);current_obj->free_list_link = next_obj;}//最后一塊current_obj = next_obj;current_obj->free_list_link = NULL;return result;
}

內存池

chunk_alloc函數內管理了一塊內存池,當refill函數要填充鏈表時,就會調用chunk_alloc函數,從內存池取出相應的內存。

在chunk_alloc函數內首先判斷內存池大小是否足夠填充一個有20個節點的鏈表,若內存池足夠大,則直接返回20個內存節點大小的內存塊給refill。如下:

        if (size_left >= total_size)  //內存池剩余空間滿足需求{result = start_free;start_free += total_size;return result;}

若內存池大小無法滿足20個內存節點的大小,但至少滿足1個內存節點,則直接返回相應的內存節點大小的內存塊給refill;

        else if (size_left >= size)  //剩余空間不能全部滿足,但至少滿足一塊{nobjs = size_left / size;result = start_free;start_free += nobjs * size;return result;}

若內存池連1個內存節點大小的內存塊都無法提供,則chunk_alloc函數會將內存池中那一點點的內存大小分配給其他合適的鏈表,然后去調用malloc函數分配的內存大小為所需的兩倍。若malloc成功,則返回相應的內存大小給refill;若malloc失敗,會先搜尋其他鏈表的可用的內存塊,添加到內存池,然后遞歸調用chunk_alloc函數來分配內存,若其他鏈表也無內存塊可用,則只能調用第一級空間配置器,因為第一級空間配置器有malloc失敗的出錯處理函數,最終的希望只能寄托在那里了。

如下是整個chunk_alloc函數:

template <bool threads>
char *__default_alloc<threads>::chunk_alloc(size_t size, int& nobjs)
{size_t total_size = size * nobjs;char *result;size_t size_left = end_free - start_free;if (size_left >= total_size)  //內存池剩余空間滿足需求{result = start_free;start_free += total_size;return result;}else if (size_left >= size)  //剩余空間不能全部滿足,但至少滿足一塊{nobjs = size_left / size;result = start_free;start_free += nobjs * size;return result;}else  //連一個區塊都無法滿足{if (size_left > 0)  //將殘余內存分配給其他合適的鏈表{obj *volatile *my_free_list = free_list + FREELIST_INDEX(size_left);((obj *)start_free)->free_list_link = *my_free_list;  //在頭部插入*my_free_list = (obj *)start_free;}size_t bytes_to_get = 2 * total_size + ROUND_UP(heap_size >> 4);start_free = (char *)malloc(bytes_to_get);if (start_free == NULL)  //堆空間不足{int i;obj *volatile *my_free_list;obj *p;for (i = size; i < __MAX_BYTES; i++){my_free_list = free_list + FREELIST_INDEX(i);p = *my_free_list;if (p != NULL){*my_free_list = p->free_list_link;start_free = (char *)p;end_free = start_free + i;return chunk_alloc(size, nobjs);}}end_free = NULL;//調用第一級配置器start_free = (char *)malloc_alloc::allocate(bytes_to_get);}heap_size += bytes_to_get;end_free = start_free + heap_size;return chunk_alloc(size, nobjs);}
}

內存釋放

第二級配置器的deallocate函數并不會直接釋放內存塊。當內存塊大小大于128字節時才會直接釋放,否則會將內存塊回收到相應的鏈表當中。如下:

void __default_alloc<threads>::deallocate(void *p, size_t n)
{//大于__MAX_BYTES,則釋放該內存if (n > (size_t)__MAX_BYTES)malloc_alloc::deallocate(p, n);obj *q = (obj *)p;obj *volatile *my_free_list;my_free_list = free_list + FREELIST_INDEX(n);//小于__MAX_BYTES,則回收區塊,并未釋放q->free_list_link = *my_free_list;*my_free_list = q;
}

內存對外接口

STL對外提供了一個simple_alloc類,該類提供統一的接口:allocate函數、deallocate函數,使得外部無需關心使用的是幾級內存配置器。另外simple_alloc類將外部所需的對象個數轉換為字節。如下。

template <typename T, typename Alloc>
class simple_alloc
{
public:static T *allocate(size_t n) // 個數{return n == 0 ? 0 : (T*)Alloc::allocate(n * sizeof(T)); // 將個數轉換為字節}static T *allocate(void){return (T*)Alloc::allocate(sizeof(T));}static void deallocate(T *p, size_t n) // 個數{if (n != 0)Alloc::deallocate(p, n * sizeof(T));}static void deallocate(T *p){Alloc::deallocate(p, sizeof(T));}
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/717862.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/717862.shtml
英文地址,請注明出處:http://en.pswp.cn/news/717862.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【自然語言處理】BitNet b1.58:1bit LLM時代

論文地址&#xff1a;https://arxiv.org/pdf/2402.17764.pdf 相關博客 【自然語言處理】BitNet b1.58&#xff1a;1bit LLM時代 【自然語言處理】【長文本處理】RMT&#xff1a;能處理長度超過一百萬token的Transformer 【自然語言處理】【大模型】MPT模型結構源碼解析(單機版)…

如何在 Mac 上成功輕松地恢復 Excel 文件

Microsoft Excel 的 Mac 版本始終略落后于 Windows 版本&#xff0c;這也許可以解釋為什么如此多的用戶渴望學習如何在 Mac 上恢復 Excel 文件。 但導致重要電子表格不可用的不僅僅是 Mac 版 Excel 的不完全穩定性。用戶有時會失去注意力并刪除錯誤的文件&#xff0c;存儲設備…

2024-03-03 c++

&#x1f338; MFC進度條控件 | Progress Control 1。新建MFC項目&#xff08;基于對話框、靜態庫&#xff09; 2。添加控件&#xff0c;刪除初始的3個多余控件 加1個progress control&#xff0c;修改其marquee為true&#xff0c;添加變量&#xff1a;變量名為test_progress。…

Angular基礎---HelloWorld---Day1

文章目錄 1. 創建Angular 項目2.對Angular架構的最基本了解3.創建并引用新的組件&#xff08;component&#xff09;4.對Angular架構新的認識&#xff08;多組件&#xff09;5.組件中業務邏輯文件的編輯&#xff08;ts文件&#xff09;6.標簽中屬性的綁定(1) ID的綁定(2) class…

String和String Builder

String和StringBuilder的區別 String類 String類代表字符串。java程序中所有字符串文字&#xff08;例如“abc”&#xff09;都被實現為此類的實例。 String類源碼是用final修飾的&#xff0c;它們的值在創建后不能被更改。字符串緩沖區支持可變字符串。 String對象是不可變…

STM32 (2)

1.stm32編程模型 將C語言程序燒錄到芯片中會存儲在單片機的flsah存儲器中&#xff0c;給芯片上電后&#xff0c;Flash中的程序會逐條進入到CPU中去執行&#xff0c;進而CPU去控制各種模塊&#xff08;即外設&#xff09;去實現各種功能。 2.寄存器和寄存器編程 CPU通過控制其…

Apache POI的簡單介紹與應用

介紹 Apache POI 是一個處理Miscrosoft Office各種文件格式的開源項目。我們可以使用 POI 在 Java 程序中對Miscrosoft Office各種文件進行讀寫操作。PS&#xff1a; 一般情況下&#xff0c;POI 都是用于操作 Excel 文件&#xff0c;如圖&#xff1a; Apache POI 的應用場景&…

SQL無列名注入

SQL無列名注入 ? 前段時間&#xff0c;隊里某位大佬發了一個關于sql注入無列名的文章&#xff0c;感覺好像很有用&#xff0c;特地研究下。 關于 information_schema 數據庫&#xff1a; ? 對于這一個庫&#xff0c;我所知曉的內容并不多&#xff0c;并且之前總結SQL注入的…

設計模式-橋接模式實踐案例

橋接模式&#xff08;Bridge Pattern&#xff09;是一種結構型設計模式&#xff0c;用于將抽象與實現分離&#xff0c;使它們可以獨立地變化。這種模式通過提供一個橋接結構&#xff0c;可以將實現接口的實現部分和抽象層中可變化的部分分離開來。 以下是一個使用 Java 實現橋…

【數據結構】_包裝類與泛型

目錄 1. 包裝類 1.1 基本數據類型和對應的包裝類 1.2 &#xff08;自動&#xff09;裝箱和&#xff08;自動&#xff09;拆箱 1.2.1 裝箱與拆箱 1.2.2 自動&#xff08;顯式&#xff09;裝箱與自動&#xff08;顯式&#xff09;拆箱 1.3 valueOf()方法 2. 泛型類 2.1 泛…

【深度學習筆記】計算機視覺——目標檢測和邊界框

目標檢測和邊界框 前面的章節&#xff08;例如 sec_alexnet— sec_googlenet&#xff09;介紹了各種圖像分類模型。 在圖像分類任務中&#xff0c;我們假設圖像中只有一個主要物體對象&#xff0c;我們只關注如何識別其類別。 然而&#xff0c;很多時候圖像里有多個我們感興趣…

某大型制造企業數字化轉型規劃方案(附下載)

目錄 一、項目背景和目標 二、業務現狀 1. 總體應用現狀 2. 各模塊業務問題 2.1 設計 2.2 仿真 2.3 制造 2.4 服務 2.5 管理 三、業務需求及預期效果 1. 總體業務需求 2. 各模塊業務需求 2.1 設計 2.2 仿真 2.3 制造 2.4 服務 2.5 管理 四、…

在vue中對keep-alive的理解,它是如何實現的,具體緩存的是什么?

對keep-alive的理解&#xff0c;它是如何實現的&#xff0c;具體緩存的是什么&#xff1f; &#xff08;1&#xff09;keep-alive有以下三個屬性&#xff1a;注意&#xff1a;keep-alive 包裹動態組件時&#xff0c;會緩存不活動的組件實例。主要流程 &#xff08;2&#xff09…

數字化轉型導師堅鵬:證券公司數字化營銷

證券公司數字化營銷 ——借力數字化技術實現零售業務的批量化、精準化、場景化、智能化營銷 課程背景&#xff1a; 很多證券公司存在以下問題&#xff1a; 不知道如何提升證券公司數字化營銷能力&#xff1f; 不知道證券公司如何開展數字化營銷工作&#xff1f; 不知道…

胎神游戲集第二期

延續上一期 一、海島奇胎 #include<bits/stdc.h> #include<windows.h> #include<stdio.h> #include<conio.h> #include<time.h> using namespace std; typedef BOOL (WINAPI *PROCSETCONSOLEFONT)(HANDLE, DWORD); PROCSETCONSOLEFONT SetCons…

Linux 安裝pip和換源

一 配置文檔 Linux和macOS&#xff1a; 全局配置&#xff1a;/etc/pip.conf 用戶級配置&#xff1a;~/.pip/pip.conf 或 ~/.config/pip/pip.conf 二 下載 和 安裝 # pip 安裝 wget https://bootstrap.pypa.io/get-pip.py python get-pip.py 三 查看和升級 pip -Vpython -m…

GO語言學習筆記(與Java的比較學習)(十一)

協程與通道 什么是協程 一個應用程序是運行在機器上的一個進程&#xff1b;進程是一個運行在自己內存地址空間里的獨立執行體。一個進程由一個或多個操作系統線程組成&#xff0c;這些線程其實是共享同一個內存地址空間的一起工作的執行體。 并行是一種通過使用多處理器以提…

Java虛擬機 - JVM

JVM的內存區域劃分 JVM它其實也是一個進程,進程運行的過程中,會從操作系統中申請一些資源.內存就是其中的一種.這些內存就支撐了java程序的運行.JVM從系統中申請的一大塊內存,會根據實際情況和使用用途來劃分出不同的空間,這個就是區域劃分.它一般分為 堆區, 棧區, 程序計數器…

springboot240基于Spring boot的名城小區物業管理系統

基于Spring boot的名城小區物業管理系統的設計與實現 摘要 當下&#xff0c;正處于信息化的時代&#xff0c;許多行業順應時代的變化&#xff0c;結合使用計算機技術向數字化、信息化建設邁進。以前相關行業對于物業信息的管理和控制&#xff0c;采用人工登記的方式保存相關數…

InnoDB存儲引擎對MVCC的實現

MVCC MVCC的目的 在搞清楚MVCC之前,我們要搞懂一個問題,MVCC到底解決的是什么問題? 我用一句話概括,那就是為了解決讀-寫可以一起的問題! 在我們的印象里,InnoDB可以讀讀并發,不能讀寫并發,或者寫寫并發 這是很正常的想法,因為如果讀寫并發的化,會有并發問題 而對于寫寫…