內(nèi)存池的一些思考與總結(jié)

allocate需要如下需求:

  1. 如何設計內(nèi)存池始鱼;
  2. 如何設計字節(jié)對齊臀防;
  3. 如何設計統(tǒng)計內(nèi)存使用情況为流;(待完成)
  4. 如何設計單元測試驗證內(nèi)存池的正確性酸茴。(待完成)

一些技術(shù)問題:

  1. malloc是已經(jīng)實現(xiàn)內(nèi)存對齊了烫映,問如何實現(xiàn)的沼本,怎么證明?(未解決)
  2. free和delete是如何實現(xiàn)的锭沟,如何知道釋放多少內(nèi)存抽兆。(未解決)

一 如何設計內(nèi)存池

內(nèi)存池一般而言,即是大塊內(nèi)存直接分配族淮,小塊內(nèi)存則先申請一個大塊內(nèi)存辫红,然后不斷地使用這個大塊內(nèi)存直至用完∽@保基本算法如下:

inline char* Arena::Allocate(size_t bytes) {
  // The semantics of what to return are a bit messy if we allow
  // 0-byte allocations, so we disallow them here (we don't need
  // them for our internal use).
  assert(bytes > 0);
  if (bytes <= alloc_bytes_remaining_) {          //是否能從可用內(nèi)存中獲取
    char* result = alloc_ptr_;
    alloc_ptr_ += bytes;
    alloc_bytes_remaining_ -= bytes;
    return result;
  }
  return AllocateFallback(bytes);                    //不能贴妻,去申請內(nèi)存
}

大塊內(nèi)存一定無法從可用內(nèi)存中獲取,小塊內(nèi)存則需要看剩余內(nèi)存大小较幌,如果剩余量小于需求揍瑟,則也需要申請,否則乍炉,不需要申請绢片。

所以小塊內(nèi)存共用的大塊內(nèi)存的使用情況,需要使用如下兩個變量來標識:

  // Allocation state
  char* alloc_ptr_;                                            //大塊內(nèi)存的可用位置岛琼;
  size_t alloc_bytes_remaining_;                    //當前大塊內(nèi)存還剩余多少

簡化了小塊內(nèi)存的分配底循,大塊內(nèi)存的分配邏輯如下:

char* Arena::AllocateFallback(size_t bytes) {
  if (bytes > kBlockSize / 4) {
    // Object is more than a quarter of our block size.  Allocate it separately
    // to avoid wasting too much space in leftover bytes.
    char* result = AllocateNewBlock(bytes);
    return result;
  }

  // We waste the remaining space in the current block.
  alloc_ptr_ = AllocateNewBlock(kBlockSize);
  alloc_bytes_remaining_ = kBlockSize;

  char* result = alloc_ptr_;
  alloc_ptr_ += bytes;
  alloc_bytes_remaining_ -= bytes;
  return result;
}

char* Arena::AllocateNewBlock(size_t block_bytes) {        //所有申請內(nèi)存的操作都在這;
  char* result = new char[block_bytes];
  blocks_.push_back(result);
  memory_usage_.NoBarrier_Store(
      reinterpret_cast<void*>(MemoryUsage() + block_bytes + sizeof(char*)));
  return result;
}

申請大塊內(nèi)存有兩種情況:
(1)用戶本身申請的就是大內(nèi)存槐瑞;
(2)用戶申請的是小內(nèi)存熙涤,但是小內(nèi)存所使用的大內(nèi)存塊用完了。

對于第一種情況,直接調(diào)用AllocateNewBlock方法祠挫,申請出那么大的內(nèi)存來并交給用戶就可以了那槽。
對于第二種情況,調(diào)用完AllocateNewBlock方法申請固定大小內(nèi)存(kBlockSize)等舔,還需要設置 alloc_ptr_和alloc_bytes_remaining_變量骚灸;最后在這塊新申請的大內(nèi)存中分配內(nèi)存給用戶。

內(nèi)存的釋放是統(tǒng)一在一起進行的慌植,位于分配器的析構(gòu)函數(shù)中:

Arena::~Arena() {
  for (size_t i = 0; i < blocks_.size(); i++) {
    delete[] blocks_[i];
  }
}

blocks_定義如下:

  // Array of new[] allocated memory blocks
  std::vector<char*> blocks_;

blocks_包含了所有AllocateNewBlock方法申請的內(nèi)存甚牲,最后統(tǒng)一釋放。

一些問題:

  1. 小內(nèi)存共用的大塊內(nèi)存是多大蝶柿,即一次申請多大合適丈钙?(相關(guān)的論文查詢
    leveldb中是這個大小:
static const int kBlockSize = 4096;
  1. 申請多大的內(nèi)存適合直接返回給用戶交汤,而不是共用大內(nèi)存塊雏赦?
    這種分為兩種情況:
    (1)存在可用大塊內(nèi)存時,只要大塊內(nèi)存夠用蜻展,就都是直接返回給用戶喉誊;
    (2)不存在可用的大塊內(nèi)存時,超過kBlockSize的1/4纵顾,就直接申請相應大小的內(nèi)存返回給用戶伍茄,而不是當作小內(nèi)存來申請kBlockSize大小的內(nèi)存。

  2. 以上策略存在內(nèi)存的浪費施逾,具體表現(xiàn)為:
    當用戶新申請的內(nèi)存不在可用內(nèi)存大小范圍內(nèi)敷矫,且小于kBlockSize時,那一段可用內(nèi)存就被浪費了汉额,去新申請新的內(nèi)存了曹仗, alloc_ptr_從新內(nèi)存開始。

  3. 內(nèi)存是最后統(tǒng)一釋放的蠕搜,所以是不是需要一些策略來提前釋放以節(jié)約內(nèi)存怎茫?具體的釋放時機是什么?

二 如何設計字節(jié)對齊

malloc分配的內(nèi)存一定是對齊的妓灌,含義是其返回的地址一定是2的冪次(內(nèi)存對齊的大小一般是指針的大小轨蛤,指針的大小一定是2的冪次)。

所以對齊的大小align求法:

const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;

對齊之后的地址一定滿足(align - 1的值中1的個數(shù)代表了返回值指針最后的0的個數(shù)):

  assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0);

具體算法如下:

char* Arena::AllocateAligned(size_t bytes) {
  const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;
  assert((align & (align-1)) == 0);   // Pointer size should be a power of 2
  size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-1);   //alloc_ptr_的后n位虫埂,n為對齊的位數(shù)    
  size_t slop = (current_mod == 0 ? 0 : align - current_mod);                    //對齊需要空余的字節(jié)數(shù)
  size_t needed = bytes + slop;
  char* result;
  if (needed <= alloc_bytes_remaining_) {          //類似于上一小節(jié)的Allocate
    result = alloc_ptr_ + slop;
    alloc_ptr_ += needed;
    alloc_bytes_remaining_ -= needed;
  } else {
    // AllocateFallback always returned aligned memory
    result = AllocateFallback(bytes);              //調(diào)用malloc的一定是內(nèi)存對齊的祥山;
  }
  assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0);
  return result;
}

函數(shù)同樣分為兩部分來處理:
(1)申請內(nèi)存大小 + 對齊字節(jié)數(shù) > 可用內(nèi)存大小,此時直接調(diào)用malloc去申請(此時可能會造成內(nèi)存的浪費)掉伏,此時一定是內(nèi)存對齊的缝呕;
(2)申請內(nèi)存大小 + 對齊字節(jié)數(shù) < 可用內(nèi)存大小澳窑,手動對齊:將alloc_ptr_調(diào)整到對齊位置,作為返回結(jié)果供常,再調(diào)整alloc_ptr_到下一可用位置摊聋。其中對齊的結(jié)果是返回值對齊的,最后的alloc_ptr_不是對齊的栈暇。

三 如何設計統(tǒng)計內(nèi)存使用情況栗精;

內(nèi)存的使用需要進行統(tǒng)計,以方便進行監(jiān)控以及內(nèi)存泄露的查詢瞻鹏。

內(nèi)存的統(tǒng)計要求所有的內(nèi)存申請工作都需要在同一個函數(shù)中進行:AllocateNewBlock。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹿寨,一起剝皮案震驚了整個濱河市新博,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脚草,老刑警劉巖赫悄,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異馏慨,居然都是意外死亡埂淮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門写隶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倔撞,“玉大人,你說我怎么就攤上這事慕趴』居” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵冕房,是天一觀的道長躏啰。 經(jīng)常有香客問我,道長耙册,這世上最難降的妖魔是什么给僵? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮详拙,結(jié)果婚禮上帝际,老公的妹妹穿的比我還像新娘。我一直安慰自己溪厘,他們只是感情好胡本,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著畸悬,像睡著了一般侧甫。 火紅的嫁衣襯著肌膚如雪珊佣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天披粟,我揣著相機與錄音咒锻,去河邊找鬼。 笑死守屉,一個胖子當著我的面吹牛惑艇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拇泛,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼滨巴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了俺叭?” 一聲冷哼從身側(cè)響起恭取,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎熄守,沒想到半個月后蜈垮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡裕照,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年攒发,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晋南。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡惠猿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出负间,到底是詐尸還是另有隱情紊扬,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布唉擂,位于F島的核電站餐屎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏玩祟。R本人自食惡果不足惜腹缩,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望空扎。 院中可真熱鬧藏鹊,春花似錦、人聲如沸转锈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撮慨。三九已至竿痰,卻和暖如春脆粥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背影涉。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工变隔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蟹倾。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓匣缘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鲜棠。 傳聞我的和親對象是個殘疾皇子肌厨,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容