ARM64

With the iPhone5S, apple has already switched to an arm 64-bit processor. Generally speaking, the number of bits of the processor is generally related to the integer register size and the width of the pointer. Modern cpus generally have the same size. So 64-bit generally means that the CPU has 64-bit integer registers and 64-bit wide Pointers.

But I still don’t quite understand why these two sizes have to be the same size. Doubling the number of registers and width is a significant performance boost. Doubling the pointer width just seems like a waste of more memory? It’s improved the address space, but it’s really improved by a lot. 32-bit CPU, available address space of 2^32, or 4G, for 1byte per storage unit, which is a maximum of 4GB of memory. Until this year, the iPhone11 Pro was just over that size. 64-bit wide pointer, memory address space up to 2^64, that is, 17,179,877,980 gb of addresses, far more than the required address space. Doubling the size of the pointer makes the program needlessly require more memory to run.

So, although Pointers have 64 bits, not all bits are actually used. Mac OS X uses 47 bits, while iOS on mobile only uses 33 bits. So what’s Apple doing with 64-bit addresses with redundant Pointers

// Memory is like an array with addresses ranging from 0 to 0X01 FFFFFFFF
// Each address contains one storage unit, one storage unit is 8 bits, one byte
// So 33 bits of 8GB = 8G * 1byte
memory = [
                0x00 00 00 00 00 00 00 00: [0.1.0.1.0.0.0.0].0x00 00 00 00 00 00 00 01: [0.1.0.1.0.0.0.0].0x00 00 00 00 00 00 00 02: [0.1.0.1.0.0.0.0].0x00 00 00 00 00 00 00 03: [0.1.0.1.0.0.0.0].0x00 00 00 00 00 00 00 04: [0.1.0.1.0.0.0.0].0x00 00 00 00 00 00 00 05: [0.1.0.1.0.0.0.0].0x00 00 00 00 00 00 00 06: [0.1.0.1.0.0.0.0].0x00 00 00 00 00 00 00 07: [0.1.0.1.0.0.0.0].0x00 00 00 00 00 00 00 08: [0.1.0.1.0.0.0.0],... .0x00 00 00 01 FF FF FF F9:[0.1.0.1.0.0.0.0].0x00 00 00 01 FF FF FF FA:[0.1.0.1.0.0.0.0].0x00 00 00 01 FF FF FF FB:[0.1.0.1.0.0.0.0].0x00 00 00 01 FF FF FF FC:[0.1.0.1.0.0.0.0].0x00 00 00 01 FF FF FF FD:[0.1.0.1.0.0.0.0].0x00 00 00 01 FF FF FF FE:[0.1.0.1.0.0.0.0].0x00 00 00 01 FF FF FF FF:[0.1.0.1.0.0.0.0]]Copy the code

The approximate distribution of the memory address space is as follows, address only represents the method from low to high, stack address is used from high to low, heap from low to high:

0x000000 00 00 00 00 00 keep
0x000000 00 00 00 F0 00 _Text
0x000000 00 00 0F 00 00 _Data
0x000000 00 0F 00 00 00 String constant
0x000000 00 0F 0F 00 00 Uninitialized data
0x000000 00 0F F0 00 00 Initialize data
0x000000 00 0F FF 00 00 The heap area
0x000000 00 FF 00 F0 00 The stack area
0x000000 01 FF FF FF FF The kernel area

isa

What is an object in Objective-C?

typedef struct objc_object *id;

structobjc_object { private: isa_t isa; .void initInstanceIsa(Class cls, bool hasCxxDtor);// The object is initialized
}
Copy the code

OC uses id to represent ObjC objects, and you can see that the id pointer isa structure that points to an isa field. Isa on a 32-bit CPU is actually the address of its class structure, which contains the address of the parent class and the address of the method.

In ARM64, we say that an address is actually only 33 bits long. If ISA saves the 64-bit address 0x00000001 A5FC91F0 in its entirety, the first 28 bits must be 0 and the next three bits must be 0. Only addresses divisible by 4 are accessed), obviously it is a waste to keep the same 31 zeros in each object.

define ISA_MASK        0x0000000ffffffff8ULL
union isa_t 
{
    Class cls;
    uintptr_t bits;
    struct {
        uintptr_t indexed           : 1;// a value of 0 is the direct address, and a value of 1 contains other information
        uintptr_t has_assoc         : 1;//1 Displays the objc_setAssociate object
        uintptr_t has_cxx_dtor      : 1;//1 indicates the c++ destruct function
        uintptr_t shiftcls          : 33; / / address
        uintptr_t magic             : 6;// Check whether initialization is performed
        uintptr_t weakly_referenced : 1;//1 indicates that the object has a weak pointer
        uintptr_t deallocating      : 1;// The object is being released
        uintptr_t has_sidetable_rc  : 1;// Determine whether the object reference count is too large
        uintptr_t extra_rc          : 19;// Additional pointer references to this object
    };
}
Copy the code

Isa is isa_t, which isa union type. Union means that the fields in here refer to the same memory space, but they are treated differently.

We can see the 33 bit length shiftcls, which is the real address of the class. Why is it in position 4? Because the object address is 8-bit aligned, that is, the lowest three bits must be 0, use the lower three bits to store special bits, to obtain the real address, and the ISA_MASK with the lowest three bits is 0 can directly obtain the real address.

We can see that ordinary objects have an ISA, which is 8 bytes, and then store the class attribute values. So how much space does an NSNumber object take to store the number 123? As you can see in the figure below, it is at least 20 bytes, because the object is actually allocated in increments of 16 bytes, which is actually at least 24 bytes.

It’s a bit of a waste of space, so how does Apple save on this situation

Tagged Pointer

Apple’s answer is to mark the pointer. Let’s first look at what the result of using a marker pointer is, 8 bytes.

As we can see from the above address, this is clearly not a normal memory address. The higher order of 28 is no longer 0. Yes, this is the token pointer, which represents the object itself, so you don’t have to allocate space in the heap to create the object.

  //ObjC native Tagged Pointer Supported class with index offset
    OBJC_TAG_NSString          = 2, 
    OBJC_TAG_NSNumber          = 3, 
    OBJC_TAG_NSIndexPath       = 4, 
    OBJC_TAG_NSManagedObjectID = 5, 
    OBJC_TAG_NSDate            = 6.Copy the code

We now have an NSNumber object with a value of 1, the printed address gets 0x91F75f31d1A0F417, 63 bits 1 for TaggedPointer,60 to 62 bits for type, and 0 to 59 bits for data

About Class Objects

In ObjC, a class is also an object, a class object.

typedef struct objc_class *Class;

struct objc_class : objc_object {
    // Class ISA; //8byte
    Class superclass;  //8byte
    cache_t cache;      // Method cache, 16 bytes
    uintptr_t bits;    //8byte, class_rw_t, method, attribute, protocol, etc
}

struct cache_t {
    struct bucket_t *_buckets;
    uint32_t _mask; // The total number of buckets allocated to cache
    uint32_t _occupied; // The actual number of cache buckets currently occupied.
}
struct bucket_t {
private:
    uintptr_t _key; 
    IMP _imp;
}
Copy the code

As you can see from the above definition, class objects inherit from Objc_Object. It also contains the parent class, method cache, and bit fields. The total size is 40 bytes.

NSLog(@" Actual memory size required by NSObject object: %zd", class_getInstanceSize(NSObject.class));/ / 8
NSLog(@" Actual allocated memory size of NSObject object: %zd", malloc_size((__bridge const void *)(fatherClas)));/ / 16

Class objectMeta = objc_getMetaClass("NSObject");
NSLog(@" Actual memory size required by class Object: %zd", class_getInstanceSize(objectMeta));/ / 40
Copy the code

The Class object is an instance of its corresponding MetaClass, so if we print the instance size of MetaClass to get the size of the Class, it will print exactly 40.

The resources

1. First day in objective-C Runtime — ISA and Class

2. Reading objc_msgSend

3.Mike Ash:Let’s Build Tagged Pointers

4.Introduction to 64-Bit Transition Guide