__mt_alloc源碼分析(5)

__pool<false>的內存分

本節研究__pool<false>在分配內存時做的事情。回憶一下__mt_alloc:: allocate函數的內容,一般情況下用戶通過某個bin_M_first[0]可以得到想要的內存,但是在_M_first[0]0時,__mt_alloc:: allocate調用了:

 

701       // Null, reserve.

702       __c = __pool._M_reserve_block(__bytes, __thread_id);

 

那麼_M_reserve_block做了些什麼?我們來看一下。

 

<mt_allocator.cc>

117    char*

118    __pool<false>::_M_reserve_block(size_t __bytes, const size_t __thread_id)

 

函數_M_reserve_block的原型,參數__bytes是用戶申請的內存字節數,__thread_id是當前線程分配的id。在單線程__pool<false>裏,__thread_id總是爲0

 

119    {

120      // Round up to power of 2 and figure out which bin to use.

121      const size_t __which = _M_binmap[__bytes];

122      _Bin_record& __bin = _M_bin[__which];

123      const _Tune& __options = _M_get_options();

124      const size_t __bin_size = (__options._M_min_bin << __which)

125                    + __options._M_align;

 

__which指出了__bytes字節數歸哪個bin管理,於是“(__options._M_min_bin << __which)”得到的就是這個bin管理的字節數上限。但是每個空閒塊除了數據區,還有頭信息,所以需要加上__options._M_align。最終__bin_size等於每個空閒塊的字節大小。

 

126      size_t __block_count = __options._M_chunk_size - sizeof(_Block_address);

127      __block_count /= __bin_size;     

 

可調參數_M_chunk_size__pool每次從OS申請的內存塊的大小”,那麼在這塊內存上能建立起多少個空閒內存塊呢?上面的代碼就是計算這個的。

注意到剛開始__block_count的值是_M_chunk_size減去sizeof(_Block_address),這說明內存的前幾個字節被用於生成_Block_address結構。也許讀者還記得bin結構(_Bin_record)裏有一個_M_address成員變量,用於記錄所有從OS得到的內存塊鏈表,_M_address正好就是指向_Block_address的指針。

 

129      // Get a new block dynamically, set it up for use.

130      void* __v = ::operator new(__options._M_chunk_size);

131      _Block_address* __address = static_cast<_Block_address*>(__v);

132      __address->_M_initial = __v;

133      __address->_M_next = __bin._M_address;

134      __bin._M_address = __address;

 

 

OS申請大小爲_M_chunk_size內存,最前面幾個字節作爲_Block_address_M_initial指向自己,然後把這塊內存加入到_M_address鏈表中。如下圖所示。

136      char* __c = static_cast<char*>(__v) + sizeof(_Block_address);

137      _Block_record* __block = reinterpret_cast<_Block_record*>(__c);

138      __bin._M_first[__thread_id] = __block;

139      while (--__block_count > 0)

140        {

141     __c += __bin_size;

142     __block->_M_next = reinterpret_cast<_Block_record*>(__c);

143     __block = __block->_M_next;

144        }

145      __block->_M_next = NULL;

 

用剩下的內存構造出__block_count個空閒內存塊,並串連起來,最後__bin._M_first[__thread_id]會指向這個鏈表的第一個元素。

 

147      __block = __bin._M_first[__thread_id];

148      __bin._M_first[__thread_id] = __block->_M_next;

 

別忘了我們還要給用戶返回一個空閒內存塊,現在就是取出這個塊的時候。

 

150      // NB: For alignment reasons, we can't use the first _M_align

151      // bytes, even when sizeof(_Block_record) < _M_align.

152      return reinterpret_cast<char*>(__block) + __options._M_align;

 

給用戶使用的數據區在空閒塊的某個偏移位置,這在前面討論過。

 

153    }

 

__pool<false>的內存釋

當用戶使用完內存,需要調用__mt_alloc:: deallocate釋放,進而會調用__pool<false>::_M_reclaim_block歸還給內存池。

 

<mt_allocator.cc>

102    void

103    __pool<false>::_M_reclaim_block(char* __p, size_t __bytes)

 

函數_M_reclaim_block的原型,參數__p是用戶歸還的內存地址,__bytes是數據區大小。

 

104    {

105      // Round up to power of 2 and figure out which bin to use.

106      const size_t __which = _M_binmap[__bytes];

107      _Bin_record& __bin = _M_bin[__which];

 

找到管理__bytes字節數內存塊的bin。幾乎每個操作都會涉及到這一步,由此我們知道爲什麼要用_M_binmap來映射這種關係:用空間換時間。

 

109      char* __c = __p - _M_get_align();

110      _Block_record* __block = reinterpret_cast<_Block_record*>(__c);

 

通過用戶給出的數據區地址,得到這個空閒塊的首地址,和內存分配裏給塊地址加上_M_get_align()正好配對。

 

112      // Single threaded application - return to global pool.

113      __block->_M_next = __bin._M_first[0];

114      __bin._M_first[0] = __block;

 

把空閒塊加入到_M_first[0]鏈表裏。

 

115    }

 

__pool<false>的銷

爲了與_M_initialize函數對應,__pool<false>還實現了_M_destroy函數,用於銷燬從OS動態申請的內存。不過這個函數實際上根本沒有被調用,包括析構函數裏也沒有。所以__pool仍和其他許多內存池一樣,讓OS自動回收進程裏的所有內存,而不是調用_M_destroy來歸還。

 

<mt_allocator.cc>

81     void

82     __pool<false>::_M_destroy() throw()

83     {

84       if (_M_init && !_M_options._M_force_new)

 

如果沒有初始化,或者_M_force_newtrue,就不會進行任何輔助內存的申請,這從_M_initialize裏可以看出來。

 

85         {

86      for (size_t __n = 0; __n < _M_bin_size; ++__n)

87        {

88          _Bin_record& __bin = _M_bin[__n];

89          while (__bin._M_address)

90            {

91          _Block_address* __tmp = __bin._M_address->_M_next;

92          ::operator delete(__bin._M_address->_M_initial);

93          __bin._M_address = __tmp;

 

歸還每個bin裏向OS動態申請的內存。也許_M_address的作用就在於此,雖然這裏的代碼並沒有被真正運行過。

 

94            }

95          ::operator delete(__bin._M_first);

 

歸還_M_first數組的內存。

 

96        }

97      ::operator delete(_M_bin);

98      ::operator delete(_M_binmap);

 

歸還bin數組和_M_binmap映射數組。

 

99         }

100    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章