线程局部缓存
tcmalloc采用线程局部存储技术为每一个线程创建一个ThreadCache,所有这些ThreadCache通过链表串起来。线程局部缓存有两种实现:
1. 静态局部缓存,通过__thread关键字定义一个静态变量。
2. 动态局部缓存,通过pthread_key_create,pthread_setspecific,pthread_getspecific来实现。
静态局部缓存的优点是设置和读取的速度非常快,比动态方式快很多,但是也有它的缺点。
主要有如下两个缺点:
1. 静态缓存在线程结束时没有办法清除。2. 不是所有的操作系统都支持。
ThreadCache局部缓存的实现
tcmalloc采用的是动态局部缓存,但同时检测系统是否支持静态方式,如果支持那么同时保存一份拷贝,方便快速读取。// If TLS is available, we also store a copy of the per-thread object
// in a __thread variable since __thread variables are faster to read
// than pthread_getspecific(). We still need pthread_setspecific()
// because __thread variables provide no way to run cleanup code when
// a thread is destroyed.
// We also give a hint to the compiler to use the "initial exec" TLS
// model. This is faster than the default TLS model, at the cost that
// you cannot dlopen this library. (To see the difference, look at
// the CPU use of __tls_get_addr with and without this attribute.)
// Since we don't really use dlopen in google code -- and using dlopen
// on a malloc replacement is asking for trouble in any case -- that's
// a good tradeoff for us.
#ifdef HAVE_TLS
static __thread ThreadCache* threadlocal_heap_
# ifdef HAVE___ATTRIBUTE__
__attribute__ ((tls_model ("initial-exec")))
# endif
;
#endif
// Thread-specific key. Initialization here is somewhat tricky
// because some Linux startup code invokes malloc() before it
// is in a good enough state to handle pthread_keycreate().
// Therefore, we use TSD keys only after tsd_inited is set to true.
// Until then, we use a slow path to get the heap object.
static bool tsd_inited_;
static pthread_key_t heap_key_;
尽管在编译器和连接器层面可以支持TLS,但是操作系统未必支持,因此需要实时的检查系统是否支持。主要是通过手动方式标识一些不支持的操作系统,代码如下:
thread_cache.h
// Even if we have support for thread-local storage in the compiler
// and linker, the OS may not support it. We need to check that at
// runtime. Righ