This article will analyze apple memory management related knowledge points, this article is based on the OUTPUT of arm64 architecture. It is divided into the following points

Memory layout 2. Reference counting 3. Weak/strong reference 4

Memory layout

In terms of memory layout, here are two images, with respect to originality, the source of the image is from the author’s summary of the different areas of memory layout.

X86_64: stack starts at 0x7, heap starts at 0x6, static constant 0x1: arm_64: stack starts at 0x1, heap starts at 0x2

**2021-09-10 14:41:54.922932+0800 001-- Demo[3358:229699] obJ stack address = 0x7ffEEb3e04d8 Memory address of the heap = 0x60000396C550 ** **2021-09-10 14:41:54.923074+0800 001-- Five large area Demo[3358:229699] a == 0x7FFEEB3e04D4 ** **2021-09-10 14:41:54.923140+0800 001-- Five regions Demo[3358:229699] b == 0x7FFeeb3e04D0 ** **2021-09-10 14:41:54.923221+0800 001 - five regional Demo [3358, 229699] * * * * * * * * * * * * static area * * * * * * * * * * * * * * * * 2021-09-10 14:41:54. 923317 + 0800 001 - five regional Demo (3358-229699) ClA == 0x10482045C ** **2021-09-10 14:41:54.923398+0800 001-- Demo[3358:229699] bssA == 0x104820460** **2021-09-10 14:41:54.923470+0800 001-- Demo[3358:229699] bssStr1 == 0x104820468** **2021-09-10 14:41:54.923570+0800 001 - five regional Demo [3358, 229699] * * * * * * * * * * * * constants district * * * * * * * * * * * * * * * * 2021-09-10 14:41:54. 923654 + 0800 001 - five regional Demo (3358-229699) ClB == 0x104820440** **2021-09-10 14:41:54.923724+0800 001-- 5-region Demo[3358:229699] bssB == 0x104820444** **2021-09-10 14:41:54.923792+0800 001-- Five regions Demo[3358:229699] bssStr2 == 0x104820448**Copy the code
2021-09-10 14:47:46.683094+0800 001-- Demo[515:92250] obJ stack address = 0x16D5F8F48, Memory address of the heap =0x281d150e0 2021-09-10 14:47:46.683186+0800 001-- 5 large area Demo[515:92250] A == 0x16D5F8f44 2021-09-10 14:47:46.683226+0800 001-- Demo[515:92250] B == 0x16D5F8F40 2021-09-10 14:47:46.683267+0800 001-- Demo[515:92250] * * * * * * * * * * * * static area * * * * * * * * * * * * 2021-09-10 14:47:46. 683302 + 0800 001 - five regional Demo (515-92250) clA = = 0 x10280d45c 2021-09-10 14:47:46.683334+0800 001-- 5 big area Demo[515:92250] bssA == 0x10280D460 2021-09-10 14:47:46.683367+0800 Demo[515:92250] bssStr1 == 0x10280D468 2021-09-10 14:47:46.683407+0800 001-- Demo[515:92250] bssStr1 == 0x10280D468 2021-09-10 14:47:46.683407+0800 001 * * * * * * * * * * * * constants district * * * * * * * * * * * * 2021-09-10 14:47:46. 683589 + 0800 001 - five regional Demo [515-92250] clB = = 0 x10280d440 2021-09-10 14:47:46.683727+0800 001-- 5 big area Demo[515:92250] bssB == 0x10280D444 2021-09-10 14:47:46.683841+0800 001-- Demo[515:92250] bssStr2 == 0x10280D448Copy the code

Only the file owner can modify static variables. Other files can only be read.

Reference counting

The reference count is stored in THE EXTRA_rc of ISA. If extra_RC is full, the has_SIDETABLE_rc hash table needs to be used when extra_RC is greater than 10

      define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t unused            : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
Copy the code

The set method triggers the objC_Object ::rootRetain to apply count processing to the strong modifier property when assigned

Clang-s-fobjc-arc-ememit – LLVM main.m -o main.ll (obj_storeStrong, clang-s-fobjc-arc-ememit)

; Function Attrs: noinline optnone ssp uwtable
define internal void @"\01-[LGTeacher setPerson:]"(%0* %0, i8* %1, %2* %2) #1 {
  %4 = alloca %0*, align 8
  %5 = alloca i8*, align 8
  %6 = alloca %2*, align 8
  store %0* %0, %0** %4, align 8
  store i8* %1, i8** %5, align 8
  store %2* %2, %2** %6, align 8
  %7 = load %2*, %2** %6, align 8
  %8 = load %0*, %0** %4, align 8
  %9 = bitcast %0* %8 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i64 16
  %11 = bitcast i8* %10 to %2**
  %12 = bitcast %2** %11 to i8**
  %13 = bitcast %2* %7 to i8*
  call void @llvm.objc.storeStrong(i8** %12, i8* %13) #3
  ret void
Copy the code

If nopointisa is not used, the refcntStorage in SideTable does a +2 operation on the hash table.

objc_object::sidetable_retain(bool locked) { #if SUPPORT_NONPOINTER_ISA ASSERT(! isa.nonpointer); #endif SideTable& table = SideTables()[this]; if (! locked) table.lock(); size_t& refcntStorage = table.refcnts[this]; if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) { refcntStorage += SIDE_TABLE_RC_ONE; } table.unlock(); return (id)this; }Copy the code

If it is nonpointerisa, determine whether the destructor is being executed. The extra_rc+1 of newisa.bits is not destructively added to carry. Extra_rc does the +1 operation on reference counting in the StoreExclusive method.

slowpath(newisa.isDeallocating()) newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++ newisa.extra_rc = RC_HALF; //extra_rc stores half of sidetable_addExtraRC_nolock(RC_HALF); StoreExclusive(&isa.bits, &oldisa.bits, newISa.bits)) extra_rc++Copy the code

Let’s look at how weak reference oc is handled. When we decorate a variable with weak, we can see that the storeWeak function is called

@property(nonatomic, weak) LGPerson *person;

; Function Attrs: noinline optnone ssp uwtable
define internal void @"\01-[LGTeacher setPerson:]"(%0* %0, i8* %1, %2* %2) #1 {
  %4 = alloca %0*, align 8
  %5 = alloca i8*, align 8
  %6 = alloca %2*, align 8
  store %0* %0, %0** %4, align 8
  store i8* %1, i8** %5, align 8
  store %2* %2, %2** %6, align 8
  %7 = load %2*, %2** %6, align 8
  %8 = load %0*, %0** %4, align 8
  %9 = bitcast %0* %8 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i64 16
  %11 = bitcast i8* %10 to %2**
  %12 = bitcast %2** %11 to i8**
  %13 = bitcast %2* %7 to i8*
  %14 = call i8* @llvm.objc.storeWeak(i8** %12, i8* %13) #3
  ret void
Copy the code

When you enter storeWeak, you can see the SideTabble structure

struct SideTable { spinlock_t slock; / / lock RefcountMap refcnts; // Hash store reference count weak_table_t weak_table; // Hash tables store weak reference countsCopy the code
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); Weak_entry_for_referent (weak_table, referent)) {append_referrer(entry, referrer);  } else { weak_entry_t new_entry(referent, referrer); Weak_grow_maybe (weak_table); // Create a new_entry entity. weak_entry_insert(weak_table, &new_entry); } struct weak_entry_t {// Struct weak_entry_t {// DisguisedPtr<objc_object> Referent; union { struct { weak_referrer_t *referrers; // Uintptr_t out_of_line_ness: 2; uintptr_t num_refs : PTR_MINUS_2; uintptr_t mask; uintptr_t max_hash_displacement; }; struct { // out_of_line_ness field is low bits of inline_referrers[1] weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; }; entry->inline_referrers[i] = new_referrer;Copy the code

Weak_table Weak reference table stores weak references of objects, and the properties of each object also have weak reference tables.

Weak-reference treatment as shown in the figure below, even if object is actually destroyed, WeakObj3 will not be affected. Weakobj3 does not manage object objects. Weak reference tables and objects are managed independently.

Automatic release tank

In the main entry, an @AutoReleasepool automatic releasepool is used to manage the running of UIApplicationMain. Let’s look at what assembly content is through Clang. Then see what function is called in assembly mode. libobjc.A.dylib`objc_autoreleasePoolPush:

int main(int argc, char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; NSLog ((nsstrings *) & __NSConstantStringImpl__var_folders_1y_3mygr0nx0c5dnvbk_r66nhtc0000gn_T_main_677664_mi_0); }}Copy the code

objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}
Copy the code

See the description for AutoReleasepoolPush, starting with 1. There is a POOL_BOUNDARY boundary management, 2 to distinguish between hot page and cool page, object usage, 3. The auto-release pool is a two-way list. The auto-release pool husband class was found, which contains the following properties

magic_t const magic; __unsafe_unretained id *next; // 8 The next position of the newly added autoRelease object pthread_t const thread; // 8 AutoreleasePoolPage * const parent; // 8 parent nil AutoreleasePoolPage *child; // uint32_t const depth; // 4 uint32_t hiwat; // 4 The maximum number of stacksCopy the code

As a simple example, let’s look at the automatic release of pool-managed objects

extern void _objc_autoreleasePoolPrint(void); int main(int argc, const char *argv[]){ @autoreleasepool { NSObject *obj = [[[NSObject alloc] init] autorelease]; _objc_autoreleasePoolPrint(); } return 0; } objc[3237]: ############## objc[3237]: AUTORELEASE POOLS for thread 0x1000ebe00 objc[3237]: 2 releases pending. objc[3237]: [0x10880a000] ................ PAGE (hot) (cold) objc [3237] : [0 x10880a038] # # # # # # # # # # # # # # # # POOL 0 x10880a038 objc / / sentinel objects [3237] : X10880a040 [0] 0 x101505760 NSObject / / management object objc [3237] : # # # # # # # # # # # # # #Copy the code

Then go to the push function, locate the autoreleaseFast, determine if the current hotPage is full, create autoreleaseNoPage if it is full, and add if it is not

    static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }
Copy the code

Partial logic of the autoreleaseNoPage function

// Install the first page. AutoreleasePoolPage *page = new AutoreleasePoolPage(nil); // create page setHotPage(page); // Set hotpage to if (pushExtraBoundary) {page->add(POOL_BOUNDARY); AutoreleasePoolPage(AutoreleasePoolPage *newParent) : AutoreleasePoolPageData(begin(), objc_thread_self(), newParent, newParent ? 1+newParent->depth : 0, newParent ? newParent->hiwat : 0) { if (objc::PageCountWarning ! = -1) { checkTooMuchAutorelease(); } if (parent) { parent->check(); ASSERT(! parent->child); parent->unprotect(); parent->child = this; parent->protect(); } protect(); }Copy the code

Think about how much data each AutoreleasePoolPage object holds and how many new pages it arrives at, add the object in a for loop, and see the output

Calculate the space of a page 504*8 + 56(itself) + 8(sentinel object) = 4096 4K. Only one sentinel object is required in an auto-release pool.

Alloc new copy mutablecopy will not be added to the auto release pool.