libstdc++ 中 std::shared_ptr 的内存开销
std::shared_ptr<T>
的内存开销
std::shared_ptr<T>
element_type* _M_ptr; // Contained pointer. sizeof(intptr_t) 字节
__shared_count<_Lp> _M_refcount; // Reference counter. sizeof(intptr_t) 字节
_Sp_counted_base<_Lp>* _M_pi;
std::_Sp_counted_base<__default_lock_policy>
// vtable pointer // sizeof(intptr_t) 字节
_Atomic_word _M_use_count; // #shared 4 字节,实际上是 int 类型
_Atomic_word _M_weak_count; // #weak + (#shared != 0) 4 字节
其中,记录 use count 是为了判断什么时候可以释放共享指针指向的对象;记录 weak count 是为了判断什么时候可以安全释放控制块本身。即便是共享指针指向对象已经被释放(use count 归零),也可能有弱指针会尝试转换成共享指针,因此应该保证这些弱指针能安全查询控制块。还有一点,如果用 std::make_shared
创建共享指针,use count 归零而 weak count 不归零时,共享对象只是被析构,其内存会等到 weak count 归零时一起被释放。可以参考 https://stackoverflow.com/a/49585948/ 。
在 64 位环境下,一个 std::shared_ptr<T>
是 16 字节(8 + 8),其中控制块的真实大小是 16 字节(4 + 4 + 8)。(在 std::shared_ptr<T>
中存储的是指向控制块的指针、而不是控制块本身,这样才能保证使用相同的控制块。)
std::_Sp_counted_base<_Lp>
的大小
不过,std::_Sp_counted_base
还支持其他的上锁策略,从 /usr/include/c++/12/bits/shared_ptr_base.h 的以下代码可以看出一共有 4 种可选的上锁类型。
using __gnu_cxx::_Lock_policy;
using __gnu_cxx::__default_lock_policy;
using __gnu_cxx::_S_single;
using __gnu_cxx::_S_mutex;
using __gnu_cxx::_S_atomic;
其中 __gnu_cxx::_S_mutex
会导致 std::_Sp_counted_base<_Lp>
继承的基类 _Mutex_base<_Lp>
从 0 字节(空基类优化)变成 40 字节(在 64 位环境下),此时 std::_Sp_counted_base<__gnu_cxx::_S_mutex>
为 56 字节。这种类型会在两个用来计数的变量(_M_use_count
和 _M_weak_count
)不能使用原子操作时被启用:此时这两个计数变量就只负责计数,同步用 mutex 来保证。在 x86 上 int 类型是可以支持原子操作的,所以默认模式不是 __gnu_cxx::_S_mutex
。
// Empty helper class except when the template argument is _S_mutex.
template<_Lock_policy _Lp>
class _Mutex_base
{
protected:
// The atomic policy uses fully-fenced builtins, single doesn't care.
enum { _S_need_barriers = 0 };
};
template<>
class _Mutex_base<_S_mutex>
: public __gnu_cxx::__mutex
{
protected:
// This policy is used when atomic builtins are not available.
// The replacement atomic operations might not have the necessary
// memory barriers.
enum { _S_need_barriers = 1 };
};
在支持整数原子操作的平台,标题中的类型的大小为 4 + 4 + sizeof(intptr_t) /* vptr */
;在不支持整数原子操作的平台,此大小为 4 + 4 + sizeof(intptr_t) + sizeof(std::mutex)
。