内存管理(四)tcmalloc2 内存释放及源码剖析

来源:互联网 发布:宜宾市行知中学周圣川 编辑:程序博客网 时间:2024/06/07 02:08

     在上一篇博文里提到了可以从未分配完的span里继续分配内存。那么,释放的时候怎么找到对应的内存呢。Page heap一共保存着两个mappagemap_记录着某一内存页对应着哪一个spanpagemap_cache记录着某一内存页对应哪一个sizeclassPagemap_的底层是radix-tree.

 

 

现在来谈谈内存释放。具体分为小块内存释放和大块内存释放。

 

 

内存释放的过程:

1.先找出要释放的内存指针在哪个内存页上;

2.PageIDpagemap_cache_找到对应的Sizeclass;

3.如果cache里没有对应ID,则去pagemap_找对应的span,然后得到Sizeclass

4.取得对应线程的Threadcache,调用函数释放

 

最简单的就是在本地线程释放,代码如下:

 

inline void ThreadCache:: Deallocate(void * ptr, size_t cl )

{

FreeList* list = &list_ [cl];

size_ += Static::sizemap ()->ByteSizeForClass( cl); //检查链表大小是否超过最大限度

ssize_t size_headroom = max_size_ - size_ - 1;

ASSERT( ptr != list ->Next());

list-> Push(ptr ); //把chunk push进去

ssize_t list_headroom = 

static_cast<ssize_t >(list-> max_length()) - list ->length();

//试着减减看,看下此链表长度是否超过最大容纳量 


if (( list_headroom | size_headroom ) < 0) //如果长度和大小中的任意一个超过了最大值,那就处理它,让他变短

{

if (list_headroom < 0)

{

ListTooLong(list , cl);

}

if (size_ >= max_size_) Scavenge();

 } 

}

 

 

接下来就是处理太长了的函数,代码如下

void ThreadCache ::ListTooLong( FreeList* list , size_t cl)

{

const int batch_size = Static:: sizemap()->num_objects_to_move (cl);

ReleaseToCentralCache( list, cl , batch_size);//从线程free list弹出制定个空闲对象插入到对应中央堆去

if ( list->max_length () < batch_size)

list->set_max_length (list-> max_length() + 1); //动态调整max_length   

//此函数为  max_length = list->max_length+1

}

else if (list ->max_length() > batch_size)

{

list->set_length_overages (list-> length_overages() + 1);

if (list ->length_overages() > kMaxOverages)

{

ASSERT(list ->max_length() > batch_size);

list->set_max_length (list-> max_length() - batch_size );

//查看有没有必要重新设置max_length

list->set_length_overages (0);

    

}

}

 

从中央堆释放内存

 

     从再分配的角度看,如果直接free的内存直接放回中央堆,下次再malloc的时候又得再切割以此。所以还是老样子:先试着看能不能放进TCEntry缓存区,如果可以,放进去就返回了。如果没有地方放就只能够放到对应的span下了。

 

释放回span

 

void CentralFreeList ::ReleaseToSpans( void* object )

{

Span* span = MapObjectToSpan (object);

ASSERT( span != NULL );

ASSERT( span->refcount > 0);

if ( span->objects == NULL) //如果span 用完了就放回nonempty_ (因为本次释放了一个对象)

 {

……

if ( false)

{

…… //assert该页里皆为空,错误报错返回

}

counter_++;

span-> refcount--;

if ( span->refcount == 0)

{

tcmalloc::DLL_Remove (span); //解锁并且将其返回到pageheap

-- num_spans_;

lock_.Unlock (); //删除记录

{

SpinLockHolder h(Static ::pageheap_lock());

Static::pageheap ()->Delete( span);

}

lock_.Lock ();

 }

else 

{

……  

}

}

 

    既然内存已经到了span了,那就要讨论在pageheap的释放。放到pageheap之后,会调用一个函数,即和邻近的空闲的内存合并放到空闲链表中,该函数如下:

void PageHeap ::MergeIntoFreeList( Span* span )

const PageID p = span-> start;

const Length n = span-> length;

Span* prev = GetDescriptor (p-1);

 

if ( prev != NULL && prev-> location == span ->location)

//如果前一个span(可能是单页也可能不是)就合并两个span

{

ASSERT(prev ->start + prev->length == p);

const Length len = prev->length ;

RemoveFromFreeList(prev );

DeleteSpan(prev );

span->start -= len;

span->length += len;

pagemap_.set (span-> start, span );

Event(span , 'L', len);

 }

Span* next = GetDescriptor (p+ n);

 

if ( next != NULL && next-> location == span ->location)

//如果后一个span(可能是单页也可能不是)就合并两个span

{

ASSERT(next ->start == p+n );

const Length len = next->length ;

RemoveFromFreeList(next );

DeleteSpan(next );

span->length += len; 

pagemap_.set (span-> start + span ->length - 1, span); 

Event(span , 'R', len);

  }

PrependToFreeList( span);

}

 

0 0
原创粉丝点击