The last article outlined the alloc process and learned how Alloc creates objects and allocates memory. In the end, three questions were raised, so today’s paper is to describe the questions left in the last one.

1. The isa pointer

From the source code, that call play rogue. So this article or to the source code to expand.

Isa.cls = CLS; So who exactly is this ISA

Click isa to go in, it’s isa_t isa, so let’s click isa_t again

union isa_t {

    isa_t() { }

    isa_t(uintptr_t value) : bits(value) { }

    Class cls;

    uintptr_t bits;

#if defined(ISA_BITFIELD)

    **struct** {

         ISA_BITFIELD;  // defined in isa.h

    };

#endif

};
Copy the code

It’s a union. That means shared memory.

You can see from above that there are properties CLS. CLS = isa.cls = CLS.

We can see that there’s a structure in there.

Click ISA_BITFIELD.


      uintptr_t has_assoc         : 1;                                       \

      uintptr_t has_cxx_dtor      : 1;                                       \

      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \

      uintptr_t magic             : 6;                                       \

      uintptr_t weakly_referenced : 1;                                       \

      uintptr_t deallocating      : 1;                                       \

      uintptr_t has_sidetable_rc  : 1;                                       \

      uintptr_t extra_rc          : 19
Copy the code

So what are these? Under arm64, they add up to 64. Anything on your mind

Is a domain. The ISA pointer has eight bytes, a total of 64-bit binaries.

For example, if we want to store a false and true result, do we need 8 bytes, 0b0000 0000 and 0b0000 0001, do we actually use 1 bit to represent the result? And the same thing is true here.

Such advantage is to save storage space, processing is also simple.

So what does that mean

1.1. A domain

attribute The size of the role
nonpointer 1 Whether to enable pointer optimization for ISA Pointers. 0 isa pure isa pointer, and 1 bit contains class information, object reference count, and so on
has_assoc 1 Flag bit of the associated object. 0 indicates none and 1 indicates existence
has_cxx_dtor 1 Whether the object has a C++ or Objc destructor
shiftcls 33 Store the value of a class pointer.
magic 6 Used by the debugger to determine whether the current object is initialized
weakly_referenced 1 Weak variable for whether the object is or has been pointed to an ARC
deallocating 1 Flags whether the object is freeing memory
has_sidetable_rc 1 Used to store carry when the object reference count is greater than 10
extra_rc 19 The actual reference-count value of the object is reduced by 1

2.calloc

Calloc analysis is done using the libmalloc library, which is why you can’t see the details of calloc when you click on calloc.

2.1. Calloc method

Let’s go straight to the source code


calloc(size_t num_items, size_t size)

{

**void** *retval;

retval = malloc_zone_calloc(default_zone, num_items, size);

**if** (retval == **NULL**) {

errno = ENOMEM;

}

**return** retval;

}
Copy the code

The size we use here is the requested memory size of the object, as stated in the previous article is 8-byte alignment.

Here we can see that the important thing is to call malloc_zone_calloc.

2.2. Malloc_zone_calloc


malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)

{
    MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);
    void *ptr;
    if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {

    internal_check();

    }
    ptr = zone->calloc(zone, num_items, size);

    if (malloc_logger) {

    malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,

    (uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);

    }

    MALLOC_TRACE(TRACE_calloc | DBG_FUNC_END, (uintptr_t)zone, num_items, size, (uintptr_t)ptr);

    return** ptr;
}
Copy the code

PTR = zone->calloc

Let’s click in

    struct _malloc_zone_t *zone, size_t num_items, size_t size); /* same as malloc, but block returned is set to zero
Copy the code

I can’t read it. I can’t read it. What do we do? Is it over?

This is an attribute function. If we print the method in the LLDB directive, we will see that it points to the function “defaut_zone_calloc”.

Let’s do a global search


{

zone = runtime_default_zone();

return zone->calloc(zone, num_items, size);

}
Copy the code

Zone ->calloc -> zone->calloc Display nano_calloc.

static** void * nano_calloc(nanozone_t *nanozone, size_t num_items, size_t size)
{
    size_t total_bytes;
    if (calloc_get_size(num_items, size, 0, &total_bytes)) {
        return** **NULL**;
    }
    if (total_bytes <= NANO_MAX_SIZE) {

        void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);

        if (p) {

            return p;

        } else {
            /* FALLTHROUGH to helper zone */
        }
    }

    malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);

    return zone->calloc(zone, 1, total_bytes);

}
Copy the code

We go straight to p = _nan_malloc_check_clear. Click in to see.

2.3. _nano_malloc_check_clear

static void * _nano_malloc_check_clear(nanozone_t *nanozone, size_t size, boolean_t cleared_requested)
{
    MALLOC_TRACE(TRACE_nano_malloc, (uintptr_t)nanozone, size, cleared_requested, 0);
    **void** *ptr;
    size_t slot_key;
    size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key); // Note slot_key is set here
    mag_index_t mag_index = nano_mag_index(nanozone);
    nano_meta_admin_t pMeta = &(nanozone->meta_data[mag_index][slot_key]);
    ptr = segregated_next_block(nanozone, pMeta, slot_bytes, mag_index);
    return ptr;
}
Copy the code

In terms of the source code, I’m going to cut a lot of it down here, because a lot of the code is misjudged, so I’m going to get rid of it, and we’re going to focus on segregated_next_block.

The previous method, which was always size, has been changed to slot_bytes, indicating that we are handling the memory size, so we can easily find the segregated_size_to_fit method.

2.4. Segregated_size_to_fit

static MALLOC_INLINE size_t segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
    size_t k, slot_bytes;
    
    if (0 == size) {
        size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
    }

    k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta

    slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size

    *pKey = k - 1; // Zero-based!

    return slot_bytes;

}
Copy the code

The point is that the code is not a bit similar to the application content byte alignment.

Let’s see how it works. The size passed in is 16, NANO_REGIME_QUANTA_SIZE is 16, SHIFT_NANO_QUANTUM is 4, so it becomes 16 + 16-1 4 to the right and then 4 to the left.

We get 31 >> 4 << 4. The figure above gives us 16.

Then we can conclude that the system allocated memory size is 16 bytes.

3. Align memory resources

From the previous alloc analysis to this one, we finally know that the object occupancy size is 8 bytes aligned and the system allocated memory size is 16 bytes aligned.

But how does this work, what is 8-byte alignment, what is 16-byte alignment, how do properties get sorted when 8-byte alignment, how does memory get optimized.

3.1. Data member alignment rules

For struct and union data members, the first data member is placed at offset 0, and the starting location of each data member is an integer multiple of the size of the member or its child members.

3.2. Structures as members

If a structure has structure members, the structure members are stored from an integer multiple of the size of the largest element in the structure.

3.3. Total size of the structure

The total size must be an integer multiple of the largest internal member.

3.4. Memory data example

You don’t know much about words, so let’s take an example.

Let’s start with a map of the size of the base type. Let’s look at an example

struct Struct1 {

    char a;     // 0

    double b;  // 8~15

    int c;     // 16~19

    short d;   // 20~21

} Struct1;
Copy the code

Char = 1, double = 8, int = 4, short = 2.

Offset starts at 0, so a is 0.

From 3.1, we know that we must start at integer multiples. B is 8, so we must start at the 8th bit, and then occupy positions from 8 to 15.

C is 4, just 16 is an integer multiple, so the station position is 16 to 19. D is 2, so 20 ~ 21.

From 3.3, we know that the internal size needs to be an integer multiple of the member, which is not enough to complement, so it needs to be aligned with 8 bytes, so the allocated memory is 24 bytes.

The system has a sizeof method to verify the size.

Maybe you have the idea, why not row after row, so that doesn’t save space. The reason is for more convenient and quick search, with space for time, increased efficiency.

4. Memory size

We create a class that has multiple properties, so how do we know how big the object is going to be and how big the system is going to allocate it to.

4.1. class_getInstanceSize

size_t class_getInstanceSize(Class cls) { if (! cls) return 0; return cls->alignedInstanceSize(); }Copy the code

By using class_getInstanceSize, we know how big the object should be.

4.2. malloc_size

    malloc_size
Copy the code

With the malloc_size method, we need to bridge to see how big the system is allocated.

5. To summarize

  1. Isa pointer is 8 bytes, a total of 64 bits, which stores pointer optimization, reference counting, release, weak Pointers, c++, associated object information.
  2. The Calloc system allocates memory for objects in 16-byte alignment.
  3. Memory alignment is used to understand how the system is allocated.

Finally, in accordance with international practice, several questions are raised

  1. Objects and classes are obviously directed, so what is the specific relationship between them?
  2. Can objects be created more than one, and can classes be created more than one
  3. What is the nature of the object?

These questions will be addressed in the next article, so stay tuned.