美文网首页
Swift内存管理初探

Swift内存管理初探

作者: 大王叫我来巡山丨 | 来源:发表于2021-06-14 18:01 被阅读0次

HeapObject

在Swift中,一个Class对象实际上就是一个HeapObject结构体指针。那么它的内存布局是怎样的呢? 首先我们先来看一下 HeapObject 的结构:

struct HeapObject {
  /// This is always a valid pointer to a metadata object.
  HeapMetadata const *metadata;

  SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;

  HeapObject() = default;

  // Initialize a HeapObject header as appropriate for a newly-allocated object.
  constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)
    , refCounts(InlineRefCounts::Initialized)
  { }

  // Initialize a HeapObject header for an immortal object
  constexpr HeapObject(HeapMetadata const *newMetadata,
                       InlineRefCounts::Immortal_t immortal)
  : metadata(newMetadata)
  , refCounts(InlineRefCounts::Immortal)
  { }

};

从中我们可以看到:
第一个字段是一个 Metadata 对象,该对象有着与OC中 isa_t 类似的作用,用来描述对象类型的(等价于 type(of:) 取得的结果)! 但本文的重点不是在这,我们继续往下看!

这才是本文的主角哦: SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS (这是一个宏定义) !

我们点开这个宏来看看这是个啥?

RefCounts<InlineRefCountBits> refCounts;

这就是Swift中引用计数 refCounts了,引用计数、弱引用、unowned 引用都与它有关!

继续深挖定义refCounts的这个类 InlineRefCounts

typedef RefCounts<InlineRefCountBits> InlineRefCounts;
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts

InlineRefCounts: 是用在常规对象以及无主引用中
SideTableRefCounts: 则是用在weak引用对象中

InlineRefCounts 被起了个别名为 RefCounts,我们看继续看下 RefCounts类结构

template <typename RefCountBits>    
class RefCounts {
  std::atomic<RefCountBits> refCounts;
  ....
}

这里省略了所有的方法和类型定义! 注意: 这是模版类,那么就要注意传进来的模版参数

首先我们来看看 InlineRefCountBits (常规对象)

typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;

template <RefCountInlinedness refcountIsInline>
class RefCountBitsT {

  friend class RefCountBitsT<RefCountIsInline>;
  friend class RefCountBitsT<RefCountNotInline>;

  static const RefCountInlinedness Inlinedness = refcountIsInline;

  typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
    BitsType;
  typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::SignedType
    SignedBitsType;
  typedef RefCountBitOffsets<sizeof(BitsType)>
    Offsets;

  BitsType bits;

  // ...

};

通过模板替换之后,InlineRefCountBits 实际上就是一个 uint64_t! 位图如下:


占位@2x.png

接下来我们在看看 SideTableRefCounts (weak引用)

首先我们先来写一段代码,看看能从汇编中发现什么?

class People {
    var age = 10
}

let p = People()
weak var p2 = p
汇编@2x.png

咦,这个swift_weakInit是什么? 它又做了什么? 我们继续往里看

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

从上述代码可以看出该对象为WeakReference类! 那么 weak 变量, 编译器则会在声明的过程中就定义了WeakReference对象,该对象主要目的就是用来管理当前的弱引用对象!
该对象调用了其中的 nativeInit(value) 方法;传递了当前的HeapObject对象

继续看 nativeInit 具体操作

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

如果当前对象不为空则调用object->refCounts.formWeakReference()方法、我们查看下 formWeakReference方法的实现

template <> HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference()
{
  auto side = allocateSideTable(true);
  if (side)
    return side->incrementWeak();
  else
    return nullptr;
}

创建SideTable、如果创建成功,则执行 incrementWeak();
查看allocateSideTable创建过程

template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting)
{
  auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
  // Preflight failures before allocating a new side table.
  if (oldbits.hasSideTable()) {
    // Already have a side table. Return it.
    return oldbits.getSideTable();
  } 
  else if (failIfDeiniting && oldbits.getIsDeiniting()) {
    // Already past the start of deinit. Do nothing.
    return nullptr;
  }
  HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
  auto newbits = InlineRefCountBits(side);
  ....
  return side;
}
  • 首先拿到原有的引用计数 refCounts.load(SWIFT_MEMORY_ORDER_CONSUME)
  • 然后通过 getHeapObject() 创建一个 HeapObjectSideTableEntry 实例对象 side
  • 再把创建出来的 side对象地址给 InlineRefCountBits 、而InlineRefCountBits 就是我们分析强引用计数的 RefCountBitsT!

顺便一起来看一下 HeapObjectSideTableEntry 的结构:

class HeapObjectSideTableEntry {
  std::atomic<HeapObject*> object;
  SideTableRefCounts refCounts;
  public:
  HeapObjectSideTableEntry(HeapObject *newObject)
    : object(newObject), refCounts()
  { }
  ......
}

在HeapObjectSideTableEntry中保留了原对象的Metadata和refCound!
还有bits(强引用、无主引用的信息) 以及新增的 weakBits(弱引用信息)

小结

swift对象都是以HeapObject为模板创建!
RefCounts:
第一种是 InlineRefCounts ,用在 常规对象(无主) 中,它其实是一个 uint64_t!
第二种是SideTableRefCounts, 用在weak对象中,此时它是一个指向 SideTable(HeapObjectSideTableEntry) 的指针。

注意点: OC与Swift区别

OC:
弱引用计数是存放在全局维护的散列表中,isa中会记录是否使用了散列表。
在引用计数为0时,自动触发dealloc,会检查并清空当前对象的散列表计数。

Swift
弱引用计数也是存放在散列表中,这个散列表是局部的! 每个weak对象都有自己的散列表!
即便一个对象在使用了弱引用后,也不能保证相关内存全部被释放! 因为SideTable 的生命周期与对象是分离的,当强引用计数为 0 时,只有 HeapObject 被释放了,但不会触发SideTable的清空。而是在下次访问时发现当前对象为nil时,才会清空SideTable

相关文章

  • Swift内存管理初探

    HeapObject 在Swift中,一个Class对象实际上就是一个HeapObject结构体指针。那么它的内存...

  • 每天学一点Swift----面向对象下(十)

    十九. Swift内存管理 1. Swift提供了强大的内存管理机制:Swift通过自动引用计数(ARC)可以很好...

  • Swift--内存管理

    Swift内存管理概述 强引用循环 打破强引用循环 闭包中的强引用循环 Swift内存管理概述 Swift中的AR...

  • swift内存管理

    Swift使用自动引用计数(ARC)机制来处理内存。通常情况下,Swift内存管理机制会自动管理内存,无须我们考虑...

  • Swift使用自动引用计数

    Swift使用自动引用计数(ARC)机制来处理内存。通常情况下,Swift内存管理机制会自动管理内存,无须我们考虑...

  • Swift 内存管理

    Swift 内存管理 [TOC] 前言 本文将介绍一下Swift中的内存管理,关于内存的一些基础知识可以参考我以前...

  • Swift Runtime-引用计数

    前言 在Swift Runtime-初探一文里,我们初步研究了对象的内存结构.有metadata及Refcount...

  • 内存管理初探

    一、概览 1.什么是内存管理 内存管理是在程序运行时,分配内存空间的过程。在Object-C当中,内存管理可以被看...

  • Swift底层原理-内存管理

    Swift底层原理-内存管理 Swift语言延续了和Objective-C语言一样的思路进行内存管理,都是采用引用...

  • Swift-内存管理,指针

    内存管理 Swift采用引用计数的ARC内存管理方案(堆空间) Swift的ARC中有3钟引用强引用弱引用(wea...

网友评论

      本文标题:Swift内存管理初探

      本文链接:https://www.haomeiwen.com/subject/mnwneltx.html