最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網 會員登陸 & 注冊

全網最詳細的內存池技術的原理與實現(秒懂)

2022-04-06 20:25 作者:補給站Linux內核  | 我要投稿

序言

  • 最近在網上看到了幾篇篇講述內存池技術的文章,有一篇是有IBM中國研發(fā)中心的人寫的,寫的不錯~~文章地址在本篇blog最后。原文的講述比我的要清晰很多,我在這只是把我的一些理解和遇到的一些問題和大家分享一下~~

一、為什么要使用內存池技術呢

  • 主要有兩個原因:

  1. 減少new、delete次數,減少運行時間;

  2. 避免內存碎片。

1、效率

  • c語言中使用malloc/free來分配內存,c++中使用new/delete來分配內存,他們的內存申請與釋放都是與操作系統(tǒng)進行交互的。具體的內容在嚴蔚敏數據結構的第八章有相關講述,主要就是系統(tǒng)要維護一個內存鏈表,當有一個內存申請過來時,根據相應的分配算法在鏈表中找個一個合適的內存分配給它。這些算法有的是分配最先找到的不小于申請內存的內存塊,有的是分配最大的內存塊,有的是分配最接近申請內存大小的內存塊。分配的內存塊可能會大于所申請的內存大小,這樣還有進行切割,將剩余的內存插入到空閑鏈表中。當釋放的時候,系統(tǒng)可能要對內存進行整理,判斷free的內存塊的前后是否有空閑,若有的話還要進行合并。此外,new/delete還要考慮多線程的情況??傊痪湓挘{用庫中的內存分配函數,十分的耗時~~

2、內存碎片

  • 什么是內存碎片內,從字面意思就很好理解了,就是內存不再是一整塊的了,而是碎了。因為連續(xù)的這種new/delete操作,一大塊內存肯能就被分割成小的內存分配出去了,這些小的內存都是不連續(xù)的。當你再去分配大的連續(xù)內存的時候,盡管剩余內存的總和可能大于所要分配的內存大小,但系統(tǒng)就找不到連續(xù)的內存了,所以導致分配錯誤。malloc的時候會導致返回NULL,而new的時候再vc6.0中返回NULL,vs2003以上則是拋出異常。

二、原理

  • 要解決上述兩個問題,最好的方法就是內存池技術。具體方法就是大小固定、提前申請、重復利用。

  • 因為內存的申請和釋放是很低效的,所以我們只在開始時申請一塊大的內存(在該塊內存不夠用時在二次分配),然后每次需要時都從這塊內存中取出,并標記下這塊內存被用了,釋放時標記此內存被釋放了。釋放時,并不真的把內存釋放給操作系統(tǒng),只要在一大塊內存都空閑的時候,才釋放給操作系統(tǒng)。這樣,就減少了new/delete的操作次數,從而提高了效率。

  • 在調用內存分配函數的時候,大部分時間所分配的內存大小都是一定的,所以可以采用每次都分配固定大小的內存塊,這樣就避免了內存碎片產生的可能。


【文章福利】小編推薦自己的Linux內核技術交流群:【891587639】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。?!前100名進群領取,額外贈送一份價值699的內核資料包(含視頻教程、電子書、實戰(zhàn)項目及代碼)?


?

三、具體實現

  • 我所采用的內存池的構造方法完全是按照文章1所介紹的方法,內存池的結構圖如下:


  • 如圖所示MemoryPool是一個內存池類,其中pBlock是一個指向了一個內存塊的指針,nUintSzie是分配單元的大小,nInitSize是第一次分配時向系統(tǒng)申請的內存的大小,nGrouSize是后面每次向系統(tǒng)申請的內存的大小。

  • MemoryBloc代表一個內存塊單元,它有兩部分構成,一部分時MemoryBlock類的大小,另一部分則是實際的內存部分。一個MemoryBlock的內存是在重載的new操作符中分配的,如下所示:

  • MemoryBlock內中,nSize代碼該內存塊的大?。ㄏ到y(tǒng)分配內存大小-MemoryBlock類的大?。?,nFree是空閑內存單元的個數,nFirst代表的是下一個要分配的內存單元的序號。aData是用來記錄待分配內存的位置的。因為要分配的內存是在new中一起向系統(tǒng)申請的,并沒有一個指針指向這塊內存的位置,但它的位置就在MemoryBlock這個類的地址開始的,所以可以用MemoryBlock的最后一個成員的位置來表示待分配內存的位置。

  • 帶分配內存中,是以nUnitSize為單位的,一個內存單元的頭兩個字節(jié)都記錄了下一個要分配的內存單元的序號,序號從0開始。這樣實際也就構成了一個數組鏈表。由MemoryBlock的構造函數來完成這個鏈表的初始化工作:

  • 在MemoryPool的Alloc()中,遍歷block鏈表,找到nFree大于0的block,從其上分配內存單元。然后將nFree減一,修改nFirst的值。

  • 在MemoryPool的Free(pFree)函數中,根據pFree的值,找到它所在的內存塊,然后將它的序號作為nFirst的值(因為它絕對是空閑的),在pFree的頭兩個字節(jié)中寫入原來nFirst的值。然后要判斷,該block是否全部為free,方法是檢測nFree * nUnitSize == nSize。若是,則向系統(tǒng)釋放內存,若不是,則將該block放到鏈表的頭部,因為該block上一定含有空隙的內存單元,這樣可以減少分配時遍歷鏈表所消耗的時間。

四、使用

  • 內存池一般都是作為一個類的靜態(tài)成員,或者全局變量。使用時,重載new操作符,使其到MemoryPool中去分配內存,而不是向系統(tǒng)申請。這樣,一個類的所以對象都在一個內存池中開辟空間。

五、代碼

  • MemoryPool.h

MemoryPool.cpp

CTest.cpp

六、問題

  • 在編寫代碼時,遇到了一些小問題,現與大家分享如下:

  1. 重載new操作符時,編譯器要求是第一個參數必須是size_t,返回值必須是void*;free的第一個參數必須是void*.

  2. 一般要在類的成員中重載new操作符,而不要重載全局的new操作符。

  3. 一個類中要是重載了一個new操作符,一定要有一個相應類型的delete操作符,可以什么都不干,但必須有,否則在構造函數失敗時,找不到對應的delete函數。

  • 例如: 

4. 帶參數的new操作符

  • 第一個nUnitSize nInitSize是new操作符的參數,該new操作符是new了一個MemoryBlock對象,在new返回的地址上構造MemoryBlock的對象。

5. 如果在類的內部不能進行靜態(tài)成員的定義的話,可以只在內部進行聲明,在外部定義:


全網最詳細的內存池技術的原理與實現(秒懂)的評論 (共 條)

分享到微博請遵守國家法律
资兴市| 衡南县| 桦川县| 耒阳市| 昭平县| 商水县| 马公市| 永城市| 电白县| 金华市| 天长市| 阿拉善左旗| 平顺县| 泸州市| 安徽省| 伽师县| 大邑县| 龙井市| 长兴县| 子长县| 万盛区| 大安市| 峨眉山市| 周宁县| 无为县| 廊坊市| 普兰县| 运城市| 子洲县| 芦山县| 谢通门县| 疏勒县| 依兰县| 青海省| 麻栗坡县| 衡东县| 中牟县| 佛坪县| 蒙城县| 扎兰屯市| 霍州市|