A little digression first

As we all know, Apple is very strict about the management of hardware resources occupied by apps, not to mention the resources occupied by apps in the background. Under normal circumstances, when using the APP, the APP is loaded from the hard disk to the memory and starts to work. When the user presses the home button, the APP is suspended and still resides in memory. In this state, the APP will not run without calling several background methods that Apple has opened up. If at this time, so that the program continues to run, it is the background state; If the current memory will be insufficient, the system will automatically remove the previously suspended APP from the memory. So we see that sometimes when we open the APP, we still have the same data from the last page when we quit, and sometimes we re-enter it from the splash screen

What is virtual memory

Virtual memory is a memory management mechanism that allows an operating system to circumvent physical RAM limitations. The virtual memory manager creates a logical or virtual memory address space for each process and allocates it into blocks of memory of the same size, called pages. The processor and the memory management unit (MMU) maintain a page table that maps the program logical address space to the hardware address of the computer’s RAM. When a program’s code accesses an address in memory, the MMU uses the page table to translate the specified logical address into a real hardware memory address. This conversion occurs automatically and transparently to the running application.

If I write this, some people will ask why do I convert virtual addresses to physical addresses? Wouldn’t it be nice to just use the physical address? Let’s look at CPU addressing first

CPU addressing mode

How does the CPU access memory? Memory can be thought of as an array, where the elements are a byte of space, and the array indexes are what are called Physical addresses. The simplest and most direct way is for the CPU to access the corresponding memory directly through a physical address, also known as physical addressing.

Physical addressing was later extended to support segmentation, increasing the range of physical addresses by adding segment registers to the CPU and changing physical addresses to “segment addresses” : “in-segment offsets”.

However, physical addressing that supports fragmentation still has some problems, one of the most serious being the lack of address space protection. Simply put, because the physical address is exposed directly, the process can access any physical address and do whatever the user process wants, which is very dangerous.

To solve this problem, modern processors use Virtual addressing, in which the CPU accesses memory by accessing a Virtual Address and then translating the physical Address. This translation is done by the Memory Management Unit (ABBREVIATED MMU) in the CPU.

The benefits of using virtual addressing are also obvious

Benefits of using virtual addressing

  1. With virtual addressing, you can protect the address space by adding some additional permissions to the translation because the translation process takes place one at a time. So for each process, the operating system can provide it with a separate, private, contiguous address space, which is called virtual memory
  2. The greatest significance of virtual memory is to protect the address space of processes, so that processes can not interfere with each other beyond their authority. For each process, the operating system can “cheat” the virtual memory, and the process can only operate on the portion of the allocated virtual memory. At the same time, the virtual memory visible to the process is a contiguous address space, which also makes it easier for programmers to manage memory
  3. For a process, its only visible part is the virtual memory allocated to it, and virtual memory may actually map to physical memory as well as any region of the hard disk. Because the read/write speed of hard disks is not as fast as that of memory, the OS preferentially uses the physical memory space. However, if the physical memory space is insufficient, the OS swaps some memory data to hard disks for storage. This is the Swap memory Swap mechanism. With memory swapping, virtual memory actually uses disk space to expand memory space compared to physical addressing

In summary, virtual memory serves several purposes: it protects the address space of each process, simplifies memory management, and expands memory space by utilizing disk space

paging

As mentioned earlier, virtual memory is mapped to physical memory. For ease of mapping and management, both virtual and house memory are divided into units of the same size. The small unit of physical memory is called a Frame, and the minimum size of virtual memory is called a Page.

In masOS and earlier versions of iOS, the paging size is 4kB. In later A7 – and a8-based systems, the virtual memory (64-bit address space) paging size in the address space became 16KB, while paging size on physical RAM remained at 4KB. Based on A9 and later systems, pages in both virtual and physical memory were 16KB

After the page has been sliced, the system also uses a page table in main memory to record the current situation of each virtual page, including whether the virtual page has been allocated to use, whether it has been cached in physical memory. A simplified page table is shown below.

Each item in a Page Table is called a Page Table Entry, and the two most important pieces of information in a PTE are a valid bit and an address.

  • When the valid bit is set, it indicates that the virtual page has been cached in physical memory. At this time, the address points to the start of the cached physical page.
  • If the valid bit is not set, the address points to the starting location of the virtual page on disk if the page has been assigned, otherwise the address is null.

Missing page

Data transfer between virtual memory and physical memory occurs precisely because of page misses.

As shown above, when the CPU accesses PTE 1 for the first time, the significant bits in the page table are still foundIs not setThat is, it is determined that the corresponding data (VP1) is still not cached in physical memory, thus triggeringMissing page exceptionFrom VP1Virtual memoryCopy toPhysical memoryIn the. As you can see in the figure, the physical memory is full and passesLRUThe algorithm replaces a physical page (let’s say VP3) in physical memory. So after a page missing exception occurs, the page table above will be updated to look like the following figure.

The above mentioned virtual memory and physical memory mapping process, let’s look at the beginning throughmmapSteps to read the file:

  1. Allocates a virtual memory area of the specified mapping size in the current user virtual memory space.
  2. Map files on disk to this memory area and wait for subsequent page scheduling on demand.
  3. When the CPU actually accesses the data, the page missing exception is triggered to copy the required data page from disk to physical memory and record the physical page address to the page table.
  4. The process accesses the file data through the physical page address obtained from the page table.

Memory paging status

The system divides the memory pages into three states:

  • Active page: The memory page has been mapped to physical memory, has been accessed recently, and is active.
  • Inactive page: an inactive page has been mapped to physical memory, but has not been accessed recently.
  • Free pages: Collection of physical memory pages that are not associated with virtual memory pages.

What problem does paging solve

  • Solve the space waste fragmentation problem: the external fragmentation problem is overcome by allocating virtual memory space and physical memory space according to a specified size, called pages, and then allocating memory according to pages.
  • Solve the problem of program size limitation: put the currently needed pages in memory, other temporarily unused pages on disk, so that a program occupies both memory and disk, its growth space is greatly increased.

When the number of available pages drops below a certain threshold, the system takes a low Memory response. In OSX, the system swaps inactive pages to hard disk, while in iOS, a Memory Warning is triggered. If your App does not handle the low Memory Warning and still uses too much Memory in the background, it may be killed.

Scenes in iOS

When we request memory from the system, the system does not directly return the address of physical memory, but a virtual memory address. From a system perspective, each process has its own private virtual memory space of the same size. This is 4GB for 32-bit devices and 18EB(1EB = 1000PB, 1PB = 1000TB) for 64-bit devices (5s or later), mapped to physical memory space.

MemoryVirtualSize {struct task_basic_info info; mach_msg_type_number_t size = (sizeof(task_basic_info_data_t) / sizeof(natural_t)); kern_return_t ret = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size); if (ret ! = KERN_SUCCESS) { return 0; } return info.virtual_size; }Copy the code

Only when the process starts using the requested virtual memory will the system map the virtual address to the physical address so that the program can use the real physical memory. However, this mapping is not one-to-one. Logical addresses may not be mapped to RAM, or there may be multiple logical addresses mapped to the same physical RAM.

  • In the first case, a Page fault is triggered when the process tries to store logical address content.
  • In the second case, multiple processes share memory

Instead of reading the entire file at once, you can read it using paged mmap(). That is, a fragment of a file is mapped to a page in the process’s logical memory. When a page is not in memory, a Page fault is triggered and the kernel reads only that page, implementing lazy loading of the file (not lazy loading in OC). This means that a __TEXT segment in a Mach-o file can be mapped to multiple processes and can be lazily loaded, with memory shared between processes.

The __DATA segment is read-write. The copy-on-write technology, COW for short, is used here. That is, when multiple processes share a page of RAM space, when a process wants to do a write operation, it will first copy the memory contents of the page, and then remap the logical address to the new RAM page. That is, the process itself has a copy of that page. This brings us to the concept of clean/ Dirty Pages. Dirty Pages contain information about the process itself, while clean pages can be regenerated by the kernel (rereading disks). So dirty pages cost more than clean pages

Multiple processes load mach-O mirrors

  • So when multiple processes load mach-O images __TEXT and __LINKEDIT are read-only and can share memory, it will read quickly.
  • If __DATA is readable and writable, it may generate a dirty page. If a clean page is detected, it can be used directly. Otherwise, it needs to re-read the DATA page. Once dirty pages have been generated, __LINKEDIT needs to notify the kernel that the current pages are no longer needed when dyld execution is complete, and can then be cleaned again when others need to use them.

concept

clean Memory

It can be simply defined as clean memory that can be written to. Read -only for developers, iOS can write or remove.

  • Memory used by the System Framework, Binary Executable
  • Files that can be released (Page Out, on iOS), including Memory mapped files (image, data, model, etc.). Memory-mapped files are usually read-only.
  • The memory that can be reclaimed and reused in the system is not allocated to physical memory immediately, but is allocated when it is needed.
  • Every framework has a _DATA_CONST segment. When the App runs on a framework, the memory corresponding to the framework’s _DATA_CONST is changed from clean to dirty.

Note: If you use the memory mapped file mechanism to load memory, you can clear it first and load it into memory when needed. So Clean Memory.

Dirty Memory

The emphasis is on memory that cannot be reused. For developers, data can be written.

  • Memory to which data is written, including all heap objects, image decoding buffers (ImageIO, CGRasterData, IOSurface).
  • The system cannot automatically reclaim the used physical memory.
  • Heap allocation, Caches, Decompressed images.
  • _DATA and _DATA_DIRTY sections for each framework.

Memory warnings in iOS only free clean memory. Because iOS considers that dirty memory has data and cannot be cleaned. Therefore, try to avoid excessive dirty memory.

Clean and Dirty examples

int *array = malloc(20000 * sizeof(int)); // Array [0] = 32 array[19999] = 64 // Step 3Copy the code
  • The first step, apply for a length of 80000 bytes of memory space, according to a page of 16KB, you need 6 pages of memory to store. When these pages are opened up, they are Clean;
  • Second, when data is written to the memory on page 1, the memory on page 1 becomes Dirty.
  • Third, when data is written to memory on the last page, the page also becomes Dirty.

Resident Memory

Physical memory that has been mapped to virtual memory. There are some “non-code execution overhead”, such as memory for system and application binary loading.

Resident Memory = Dirty Memory + Clean Memory that is loaded in pysical Memory. Resident Memory there are two kinds of Resident Memory, the system’s (dyLD_shared_cache) and our APP’s, and the following code gets our APP’s.

Resident Memory: #import < Mach /mach.h> - (int64_t)memoryResidentSize {struct task_basic_info info; mach_msg_type_number_t size = sizeof(task_basic_info_data_t) / sizeof(natural_t); kern_return_t ret = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size); if (ret ! = KERN_SUCCESS) { return 0; } return info.resident_size; }Copy the code

Note: Resident Memory includes Memory Footprint.

Memory Footprint

The actual physical memory consumed by the App. Apple recommends using the footprint command to look at the memory footprint of an application process

We’ll see that this is different, and sometimes quite different, from the memory size we see in Instruments. A Footprint is basically the Dirty part, which we can control and optimize. Xcode Navigator records roughly the same value.

#import < Mach /mach.h> - (int64_t)memoryPhysFootprint {task_vm_info_data_tvminfo; mach_msg_type_number_t count = TASK_VM_INFO_COUNT; kern_return_t ret = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vmInfo, &count); if (ret ! = KERN_SUCCESS) { return 0; } return vmInfo.phys_footprint; }Copy the code