我正在参与「启航计划」

首要学习强引证、弱引证、循环引证的解决,重点在于强引证和弱引证的底层结构

首要内容:

  1. 强引证
  2. 弱引证

1. 强引证

经过一个目标的创立,在源码中检查引证计数

1.1 创立

代码:

class  WYTeacher {
    var age: Int = 18
    var name: String = " WY"
}
var t =  WYTeacher()
var t1 = t
var t2 = t

检查:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  • 直接将目标赋值给一个变量,便是强引证
  • 能够检查此时的refCounts的值

1.2 refCounts的知道

经过在底层源码中查找HeapObject来检查目标引证的计数。 引证计数refCounts特点其本质是RefCountsInt类型,包含unit32_t Type和int32_t SignedType

1.2.1. HeapObject

代码:

structHeapObject{
HeapMetadataconst*metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
...
}

#defineSWIFT_HEAPOBJECT_NON_OBJC_MEMBERS\
InlineRefCountsrefCounts

阐明:

  • 目标在底层是HeapObject
  • 所以在源码中检查HeapObject,发现引证是SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS
  • 经过SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS的界说能够看到是refCounts的类型是InlineRefCounts

1.2.2. InlineRefCounts

代码:

typedefRefCounts<InlineRefCountBits>InlineRefCounts;

template<typenameRefCountBits>
classRefCounts{
std::atomic<RefCountBits>refCounts;
...
}

阐明:

  • 检查InlineRefCounts,发现它是RefCounts的别号
  • 而RefCounts的成员是refCounts,它是由RefCountBits决定的
  • 因而是InlineRefCounts经过InlineRefCountBits来决定里面的值

1.2.3. InlineRefCountBits

代码:

typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;

阐明:

  • 持续查找InlineRefCountBits;,发现它是RefCountBitsT的别号

1.2.4. RefCountBitsT

代码:

template<RefCountInlinednessrefcountIsInline>
classRefCountBitsT{
...
typedeftypenameRefCountBitsInt<refcountIsInline,sizeof(void*)>::Type
BitsType;
...
BitsTypebits;
...
}

template<>
structRefCountBitsInt<RefCountNotInline,4>{
//类型
typedefuint64_tType;
typedefint64_tSignedType;
};

阐明:

  • 能够看到有一个bits特点,这个特点便是Bits
  • bits其实质是将RefCountBitsInt中的type特点取了一个别号
  • 所以bits的真实类型是uint64_t即64位整型的数组,包含Type和SignedType两个成员

能够看出refCounts中其实包含有Bits成员,而这个成员是64位整型的数组。

1.3 refCounts的赋值探究

经过探究swift_allocObject,来检查引证特点的的赋值进程

1.3.1. swift_allocObject

代码:

staticHeapObject*_swift_allocObject_(HeapMetadataconst*metadata,
size_trequiredSize,
size_trequiredAlignmentMask){
...
new(object)HeapObject(metadata);
...
}

<!--结构函数-->
constexprHeapObject(HeapMetadataconst*newMetadata)
:metadata(newMetadata)
,refCounts(InlineRefCounts::Initialized)
{}

阐明:

  • 经过swift_allocObject办法来结构目标,因而在这儿会进行目标中引证特点的赋值
  • 能够看到是经过refCounts来给引证计数赋值的,传入的参数是Initialized

1.3.2. Initialized

代码:

enumInitialized_t{Initialized};

//对应的RefCounts办法
//Refcountofanewobjectis1.
constexprRefCounts(Initialized_t)
:refCounts(RefCountBits(0,1)){}

阐明:

  • 进入Initialized界说,是一个枚举
  • 其对应的refCounts办法中,看出真实干事的是RefCountBits

1.3.3. RefCountBits

代码:

template<typenameRefCountBits>
classRefCounts{
std::atomic<RefCountBits>refCounts;
...
}

阐明:

  • 进入RefCountBits界说,也是一个模板界说

1.3.4. RefCountBitsT

代码:

LLVM_ATTRIBUTE_ALWAYS_INLINE
constexpr
RefCountBitsT(uint32_tstrongExtraCount,uint32_tunownedCount)
:bits((BitsType(strongExtraCount)<<Offsets::StrongExtraRefCountShift)|
(BitsType(1)<<Offsets::PureSwiftDeallocShift)|
(BitsType(unownedCount)<<Offsets::UnownedRefCountShift))
{}

阐明:

  • 这儿便是真实的初始化地方
  • 实际上是做了一个位域操作

1.4 RefCountsBit结构

经过上文的查找,能够得到RefCountsBit结构

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  • isImmortal(0)
  • UnownedRefCount(1-31):unowned的引证计数
  • isDeinitingMask(32):是否进行释放操作
  • StrongExtraRefCount(33-62):强引证计数
  • UseSlowRC(63)

1.5 剖析SIL代码

1.5.1. 创立目标,第一次变量赋值

代码:

class  WYTeacher {
    var age: Int = 18
    var name: String = " WY"
}
var t = WYTeacher()
var t1 = t

SIL文件:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  1. 先创立一个全局的实例变量
  2. 之后创立目标
  3. 将目标赋值给全局变量

1.5.2. 第二次变量赋值

SIL文件:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  1. 先创立t1的变量
  2. 之后经过%3拿到目标
  3. 经过copy_addr将拿到的目标赋值给t1
    1. %new = load $*LGTeacher(拿到这个目标)
    2. strong_retain %new(给这个目标引证计数加一)
    3. store %new to %9(将这个目标赋给t1)
  4. copy_addr最首要的便是对这个目标做了一下strong_ratain。之后再进行赋值操作

1.5.3. swift_retain的知道

strong_retain对应的便是 swift_retain,其内部是一个宏界说,内部是_swift_retain_,其实现是对object的引证计数作+1操作。

SIL文件:

//内部是一个宏界说
HeapObject*swift::swift_retain(HeapObject*object){
CALL_IMPL(swift_retain,(object));
}

//本质调用的便是_swift_retain_
staticHeapObject*_swift_retain_(HeapObject*object){
SWIFT_RT_TRACK_INVOCATION(object,swift_retain);
if(isValidPointerForNativeRetain(object))
object->refCounts.increment(1);
returnobject;
}

voidincrement(uint32_tinc=1){
autooldbits=refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);

//constantpropagationwillremovethisinswift_retain,itshouldonly
//bepresentinswift_retain_n
if(inc!=1&&oldbits.isImmortal(true)){
return;
}
//64位bits
RefCountBitsnewbits;
do{
newbits=oldbits;
boolfast=newbits.incrementStrongExtraRefCount(inc);
if(SWIFT_UNLIKELY(!fast)){
if(oldbits.isImmortal(false))
return;
returnincrementSlow(oldbits,inc);
}
}while(!refCounts.compare_exchange_weak(oldbits,newbits,
std::memory_order_relaxed));
}

阐明:

  • 经过refCounts.increment(1);进行引证计数+1
  • 在increment办法中是经过incrementStrongExtraRefCount来实现计数的

1.5.4. incrementStrongExtraRefCount

SIL文件:

LLVM_NODISCARDLLVM_ATTRIBUTE_ALWAYS_INLINE
boolincrementStrongExtraRefCount(uint32_tinc){
//ThisdeliberatelyoverflowsintotheUseSlowRCfield.
//对inc做强制类型转换为BitsType
//其间BitsType(inc)<<Offsets::StrongExtraRefCountShift等价于1<<33位,16进制为0x200000000
//这儿的bits+=0x200000000,将对应的33-63转换为10进制,为
bits+=BitsType(inc)<<Offsets::StrongExtraRefCountShift;
return(SignedBitsType(bits)>=0);
}

阐明:

  • 经过位域进行偏移
  • 咱们知道refCounts中的62-33位是strongCount,这儿的位域意图便是将引证计数加在这儿
  • 每次的添加为1左移33位
    • 也便是每次添加0x200000000
    • 也便是BitsType(inc) << Offsets::StrongExtraRefCountShift

1.6 引证计数的核算

由于refCounts中的62-33位是strongCount,所以每次引证计数+1,都要加上0x200000000。BitsType(inc) << Offsets::StrongExtraRefCountShift就代表1<<33位。

核算:

  1. 只要t时的refCounts是 0x0000000200000003
  2. t + t1时的refCounts是 0x0000000400000003 = 0x0000000200000003 + 0x200000000
  3. t + t1 + t2 时的refCounts是 0x0000000600000003 = 0x0000000400000003 + 0x200000000

运转检查:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  • 能够看到swift中创立实例目标时默认为1
  • 能够经过CFGetRetainCount查到一个目标的引证计数

1.7 总结

  • swift中创立实例目标时默认为1
  • 强引证计数存储在refCounts中的62-33位,因而每次引证计数+1,都要加上0x200000000

2. 弱引证

首要知道弱引证的运用、以及散列表的存储结构。散列表不只存储了弱引证,还有强引证和无主引证,假如有弱引证,在refCounts中直接运用散列表的地址

2.1 弱引证的运用

代码:

class WYTeacher {
    var age: Int = 18
    var name: String = "WY"
    var stu: WYStudent?
}
class WYStudent {
    var age = 20
    var teacher: WYTeacher?
}
func test(){
    var t = WYTeacher()
    weak var t1 = t
}

阐明:

  • 给变量t赋值给弱引证t1
  • 弱引证声明的变量是一个可选值,由于在程序运转进程中是答应将当时变量设置为nil的

2.2 debug检查底层办法

检查引证计数变化:

Swift底层探索(六)Swift内存管理的源码分析

在t1处加断点,检查汇编:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  • 能够看到在底层是运用swift_weakInit来设置弱引证的
  • refCounts处存储的是一个地址

2.3 底层源码探究

2.3.1. swift_weakInit

代码:

WeakReference*swift::swift_weakInit(WeakReference*ref,HeapObject*value){
ref->nativeInit(value);
returnref;
}

阐明:

  • 检查swift_weakInit,能够看到系统本身提供了一个WeakReference类,用来进行引证计数的创立
  • 这个ref目标调用nativeInit,传入目标,就能够创立弱引证了

2.3.2. nativeInit

代码:

voidnativeInit(HeapObject*object){
    autoside=object?object->refCounts.formWeakReference():nullptr;
    nativeValue.store(WeakReferenceBits(side),std::memory_order_relaxed);
}

阐明:

  • 在办法中,经过目标的refCounts特点的formWeakReference()办法来具体实现
  • 这个在目标底层结构中能够知道有这个办法

2.3.3. formWeakReference

代码:

template<>
HeapObjectSideTableEntry*RefCounts<InlineRefCountBits>::formWeakReference()
{
//创立sideTable
autoside=allocateSideTable(true);
if(side)
//假如创立成功,则添加弱引证
returnside->incrementWeak();
else
returnnullptr;
}

阐明:

  1. 创立散列表
  2. 经过incrementWeak()给散列表添加弱引证

2.3.4. allocateSideTable

代码:

template<>
HeapObjectSideTableEntry*RefCounts<InlineRefCountBits>::allocateSideTable(boolfailIfDeiniting)
{
//1、先拿到原本的引证计数
autooldbits=refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);

//Preflightfailuresbeforeallocatinganewsidetable.
if(oldbits.hasSideTable()){
//Alreadyhaveasidetable.Returnit.
returnoldbits.getSideTable();
}
elseif(failIfDeiniting&&oldbits.getIsDeiniting()){
//Alreadypastthestartofdeinit.Donothing.
returnnullptr;
}
//Preflightpassed.Allocateasidetable.

//FIXME:customsidetableallocator
//2、创立sideTable
HeapObjectSideTableEntry*side=newHeapObjectSideTableEntry(getHeapObject());
//3、将创立的地址给到InlineRefCountBits
autonewbits=InlineRefCountBits(side);

do{
if(oldbits.hasSideTable()){
//Alreadyhaveasidetable.Returnitanddeleteours.
//Readbeforedeletetostreamlinebarriers.
autoresult=oldbits.getSideTable();
deleteside;
returnresult;
}
elseif(failIfDeiniting&&oldbits.getIsDeiniting()){
//Alreadypastthestartofdeinit.Donothing.
returnnullptr;
}

side->initRefCounts(oldbits);

}while(!refCounts.compare_exchange_weak(oldbits,newbits,
std::memory_order_release,
std::memory_order_relaxed));
returnside;
}

阐明:

  • 经过allocateSideTable构建散列表
    1. 先拿到原本的引证计数
    • 散列表也需要存储强引证的引证计数
    • 假如这个bits原本就有散列表了,就直接运用原有的散列表
    1. 创立散列表
    • 这个散列表仅仅针对当时目标的
    1. 经过InlineRefCountBits将散列表的地址赋给值newBits
    • 因而假如有弱引证,那么引证计数中存储的便是散列表地址

2.3.5. 检查弱引证的RefCountBitsT的创立进程

代码:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  • 传入的参数是HeapObjectSideTableEntry的办法,便是用来构建散列表的Bits
  • 将散列表地址经过偏移操作存储到RefCountBits内存中
  • 在这儿能够看到,除去63、62位,剩余的地方用来存储了散列表地址

2.3.6. HeapObjectSideTableEntry

代码:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  • 检查散列表的结构
  • 包含object和refCounts
  • 其实能够看做分别将目标和引证计数作为键和值
  • 这儿的引证计数表便是散列表

2.3.7. SideTableRefCountBits

代码:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  • 承继自RefCountBitsT
  • 只要一个特点weakBits,是弱引证特点
  • 在RefCountBitsT中还有一个特点,64位用来记载原有的强引证计数。

2.4 验证

代码:

Swift底层探索(六)Swift内存管理的源码分析

核算进程:

Swift底层探索(六)Swift内存管理的源码分析

阐明:

  1. 散列表地址 = refCounts 铲除63、62位 + 左移三位
  2. 上面咱们拿到了弱引证后的地址0xc000000020261de6
  3. 将62、63清零,变成0x20261DE6
  4. 然后左移3位变成0x10130EF30,这个便是散列表地址
  5. 检查散列表内容
    1. 包含两个内容,一个是目标,所以会打印目标的地址
    2. 一个是引证表,引证表包含强引证计数和弱引证计数

2.5 总结

  • 当给一个目标添加弱引证后,会创立一个散列表用来存储弱引证和强引证
  • 并且将散列表的地址存储到bits中
  • 因而关于HeapObject有两种refCounts方法
    • 没有弱引证
      • 没有弱引证,只要强引证和无主引证
      • strongCount + unonwnedCount
    • 有弱引证
      • 假如有弱引证,那么存储的是一个散列表地址
      • 而散列表中存储的是object和散列表信息
      • 散列表信息又包含了weakBits
      • 一起又由于承继RefCountBitsT,所以依然会包含强引证和无主引证。