__mt_alloc源码分析(11)

__pool<true>的内存释

多线程内存池__pool<true>的内存释放工作主要由函数_M_reclaim_block完成。

 

<mt_allocator.cc>

250    void

251    __pool<true>::_M_reclaim_block(char* __p, size_t __bytes)

 

函数原型,参数__p为用户释放的内存块地址,__bytes为内存块字节大小。

 

252    {

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

254      const size_t __which = _M_binmap[__bytes];

255      const _Bin_record& __bin = _M_bin[__which];

 

找到负责的bin

 

257      // Know __p not null, assume valid block.

258      char* __c = __p - _M_get_align();

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

 

记住用户数据区与内存块的首地址是有偏移的。

 

260      if (__gthread_active_p())

261        {

262     // Calculate the number of records to remove from our freelist:

263     // in order to avoid too much contention we wait until the

264     // number of records is "high enough".

 

根据注释,下面的代码采用了一种“转移策略”,在线程的空闲块个数达到一定“条件”的时候转移若干个块给全局空闲链表。读者可能还记得前面介绍过多线程下mt allocator转移数据块的原理,那里面说要把free计数器控制在used计数器的_S_freelist_headroom %以内,并且每次移动的块数为block_count个,block_count表示_S_chunk_size大小的内存能生成多少个空闲块。但是,现在我告诉你,这个算法已经“过时”了,至少下面的代码不是采用这个算法。

 

265     const size_t __thread_id = _M_get_thread_id();

266     const _Tune& __options = _M_get_options(); 

267     const unsigned long __limit = 100 * (_M_bin_size - __which)

268                               * __options._M_freelist_headroom;

269 

270     unsigned long __remove = __bin._M_free[__thread_id];

271     __remove *= __options._M_freelist_headroom;

272     if (__remove >= __bin._M_used[__thread_id])

273       __remove -= __bin._M_used[__thread_id];

274     else

275       __remove = 0;

276     if (__remove > __limit && __remove > __bin._M_free[__thread_id])

277       {

278         _Block_record* __first = __bin._M_first[__thread_id];

279         _Block_record* __tmp = __first;

280         __remove /= __options._M_freelist_headroom;

281         const unsigned long __removed = __remove;

 

这里计算出来的__removed,就是实际应该从线程里转移的块的个数。关于这个“转移策略”,我想在介绍完这段代码后专门研究,因为这里的空间显然不适合列举那些公式。

 

282         while (--__remove > 0)

283           __tmp = __tmp->_M_next;

284         __bin._M_first[__thread_id] = __tmp->_M_next;

285         __bin._M_free[__thread_id] -= __removed;

 

数出__removed个块,剩余的还由__bin._M_first[__thread_id]保管。

 

287         __gthread_mutex_lock(__bin._M_mutex);

288         __tmp->_M_next = __bin._M_first[0];

289         __bin._M_first[0] = __first;

290         __bin._M_free[0] += __removed;

291         __gthread_mutex_unlock(__bin._M_mutex);

 

__removed个块加入到全局空闲链表里。注意这里需要给bin加锁,因为要修改bin里的全局数据。

 

292       }

293 

294     // Return this block to our list and update counters and

295     // owner id as needed.

296     --__bin._M_used[__block->_M_thread_id];

 

__block指向的是用户现在归还的那个块,现在把分配这个块的线程的used计数器减1。由于内存块可能在线程间传递,所以__block->_M_thread_id可能不是当前线程的id。虽然我不是很肯定,但是这里显然使用了一个假设:第296行代码是原子的执行的。

 

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

299     __bin._M_first[__thread_id] = __block;

300    

301     ++__bin._M_free[__thread_id];

 

把用户释放的块加入当前线程的空闲链表里。

 

302      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值