OC的内存管理中引用计数表非常重要, oc对象的引用计数值可能存储在两个地方, 分别是isa
中的引用计数位, 还有可能存储在全局的sideTable的refcnts
中, 前文中介绍过 :
struct SideTable {
spinlock_t slock; // 自旋锁, 用来保护 refcnts 和 weak_table 的细粒度锁
RefcountMap refcnts; // 额外的引用计数表 -- 散列Map
weak_table_t weak_table; // 弱引用表成员 -- 散列表
... // 一些c++ 构造函数, 析构, 锁方法等
}
// RefcountMap disguises its pointers because we
// don't want the table to act as a root for `leaks`.
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
1. DenseMap的介绍
这里比较关键的是DenseMap
的理解!!! 一下是LLVM的官方文档对DenseMap的介绍
llvm/ADT/DenseMap.h
DenseMap is a simple quadratically probed hash table. It excels at supporting small keys and values: it uses a single allocation to hold all of the pairs that are currently inserted in the map. DenseMap is a great way to map pointers to pointers, or map other small types to each other.
There are several aspects of DenseMap that you should be aware of, however. The iterators in a DenseMap are invalidated whenever an insertion occurs, unlike map. Also, because DenseMap allocates space for a large number of key/value pairs (it starts with 64 by default), it will waste a lot of space if your keys or values are large. Finally, you must implement a partial specialization of DenseMapInfo for the key that you want, if it isn’t already supported. This is required to tell DenseMap about two special marker values (which can never be inserted into the map) that it needs internally.
DenseMap’s find_as() method supports lookup operations using an alternate key type. This is useful in cases where the normal key type is expensive to construct, but cheap to compare against. The DenseMapInfo is responsible for defining the appropriate comparison and hashing methods for each alternate key type used.
其中有几个关键信息:
- DenseMap(稠密映射)是LLVM自定义的关系类的容器, 我们可以像
std::map
一样使用它!!! - 它的本质是一个简单的二次探测哈希表, 或者更加精确的来说, 是一个HashMap, 存储的内容是 pairs
- 它比较适合小的key-value数据
- 它使用单一分配方式一次性分配所有bucket的内存(用来存储key-value pair), 因此具有较好的局部性.
- 由于其预分配内存的原因, 扩增哈希表会导致重新分配内存与拷贝(否则无法维护连续内存的特性), 因此增删元素会导致迭代器失效(这点与
std::map
不同)
template <typename DerivedT, typename KeyT, typename ValueT, typename KeyInfoT, typename BucketT>
class DenseMapBase : public DebugEpochBase {
protected:
DenseMapBase() = default;
};
template <typename KeyT, typename ValueT,
typename KeyInfoT = DenseMapInfo<KeyT>,
typename BucketT = llvm::detail::DenseMapPair<KeyT, ValueT>>
class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
KeyT, ValueT, KeyInfoT, BucketT> {
friend class DenseMapBase<DenseMap, KeyT, ValueT, KeyInfoT, BucketT>;
using BaseT = DenseMapBase<DenseMap, KeyT, ValueT, KeyInfoT, BucketT>;
BucketT *Buckets; // 底层存储 pair 的数组
unsigned NumEntries; // 当前已经使用的条目个数
unsigned NumTombstones; // 当前被废弃且未重新映射的条目个数
unsigned NumBuckets; // 当前数组个数
public:
explicit DenseMap(unsigned InitialReserve = 0) { init(InitialReserve); }
~DenseMap() {
this->destroyAll();
deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT));
}
void init(unsigned InitNumEntries) {
auto InitBuckets = BaseT::getMinBucketToReserveForEntries(InitNumEntries);
if (allocateBuckets(InitBuckets)) {
this->BaseT::initEmpty();
} else {
NumEntries = 0;
NumTombstones = 0;
}
}
// 初始化分配 buckets 数组
bool allocateBuckets(unsigned Num) {
NumBuckets = Num;
if (NumBuckets == 0) {
Buckets = nullptr;
return false;
}
Buckets = static_cast<BucketT *>(allocate_buffer(sizeof(BucketT) * NumBuckets, alignof(BucketT)));
return true;
}
/// Returns the number of buckets to allocate to ensure that the DenseMap can
/// accommodate \p NumEntries without need to grow().
unsigned getMinBucketToReserveForEntries(unsigned NumEntries) {
// Ensure that "NumEntries * 4 < NumBuckets * 3"
if (NumEntries == 0)
return 0;
// +1 is required because of the strict equality.
// For example if NumEntries is 48, we need to return 401.
return NextPowerOf2(NumEntries * 4 / 3 + 1);
}
};
代码中能看到:
DenseMap
继承自DenseMapBase
, 它的父类定了了常规的Map
的通用操作.DensMap
数据存储在成员变量BucketT *Buckets;
数组中, 其他继承成员变量NumEntries
... 可以参考注释DenseMap
通过预分配方式申请内存, 在插入元素时在已分配的地址上构造元素. 预分配的方式是申请一块连续内存, 当需要扩容时会重新申请内存并发生内容拷贝.DenseMap
使用二次探测法解决hash碰撞问题!!!
再来看引用计数表:
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
Key是: DisguisedPtr<objc_object>
类型
value是: size_t
类型. 最终使用其中的几个位作为额外的引用计数的值(与isa
中的引用计数位类似)