WWDC 2018 Session 416: iOS Memory Deep Dive

For more WWDC 18 articles, head over to the xSwiftGG WWDC 18 Topics directory

Author: Gao Teacher, pure goodness has an ideal iOS development

The introduction

Memory resources are limited for the devices on which our App depends. Reducing the amount of memory used by an App can improve performance and experience; conversely, too much memory can cause an App to be forced out of the system. So every iOS developer should be concerned about memory. There is not much new content in this section, which is basically some old knowledge points.

Following the conventions of sessions, let’s take a look at the outline:

  • Why reduce memory usage
  • Memory footprint
  • Tools for analyzing memory usage
  • image
  • In the background, the memory is optimized
  • Demonstrate the Demo

So let’s start in order!

Why reduce memory

Before we get into memory, we need to know why we want to reduce it. The simple answer is better user experience: faster startup speed, no Crash caused by excessive memory, longer App survival, etc.

Memory footprint

Not all apps have the same memory footprint. Before moving on to the Memory usage of apps on iOS, let’s talk about Pages Memory.

Pages Memory

Memory is managed by the system and is generally divided in pages. On iOS, each page contains 16KB of space. A piece of data may occupy multiple pages of memory, and the total number of pages used is multiplied by the space per page to obtain the total memory used for the piece of data.

Memory pages can be classified as Clean or Dirty based on their allocation and usage status.

Take the above code as an example, apply for a piece of memory space with a length of 80000 bytes, according to the calculation of a page of 16KB, it needs 6 pages of memory to store.

  • When these pages are opened up, they are allClean
  • When data is written to memory on page 1, page 1 memory becomesDirty
  • When data is written to memory on the last page, the page also becomesDirty

Memory-mapped file

When the App accesses a file, the system kernel takes care of scheduling, loading and mapping the file from disk into memory. If this file is read-only, the memory pages it occupies are Clean.

As shown in the figure below, when a 50KB image is loaded into memory, 4 pages of memory are allocated for storage. In the fourth page, 2KB space will be used to store the data of this image, and the remaining space may be used to store other data.

Typical APP memory type

When the memory is insufficient, the system uses a certain policy to free up more space. A common method is to move some low-priority data to disks. This operation is called Page Out. Later, when the data is accessed again, the system is responsible for moving it back into memory. This operation is called Page In.

However, for mobile devices, frequent I/O operations on disks reduce the service life of storage devices. Starting from iOS7, the system starts to compress Memory to release Memory space. The Compressed Memory is called Compressed Memory. There are three types of Memory in iOS apps: Clean Memory, Dirty Memory, and Compressed Memory.

Clean Memory

Clean Memory refers to Memory that can be used for Page Out, including files that have been loaded into Memory, or frameworks used by the App. Each frameworks has a _DATA_CONST section, and when the App uses a framework at runtime, its _DATA_CONST memory changes from Clean to Dirty.

Dirty Memory

Dirty Memory refers to Memory that has been written to by the App, including all heap objects, image decoding buffers, and, like Clean Memory, frameworks used by the App. Every framework has a _DATA segment and a _DATA_DIRTY segment whose memory is Dirty.

It is important to note that Dirty Memory is generated during the use of the Framework. Using singletons or global initializers is a good way to reduce Dirty Memory because singletons are not destroyed once created and global initializers are performed at class load time.

Compressed Memory

When the memory is insufficient, the system compresses unused memory until the next access.

For example, when we use Dictionary to cache data, assuming that we have already used 3 pages of memory, it may be compressed to 1 page when not accessed, and then decompressed to 3 pages when used again.

Memory Warnings

Not all memory warnings are caused by apps. For example, on devices with low memory, memory warnings can occur when you answer a phone call. Traditionally, you might do something to free up memory when you receive a memory warning notification. However, the memory compression mechanism can make things complicated. Let’s take a look at this example:

Assume that the cache in your code has been compressed

In fact, when you try to access the cache object again, the system decompresses the memory

This process increases memory usage, which is not desirable when memory is tight. Later, when we do a lot of work to clean the cache, we end up with the same amount of memory as we did with memory compression

Therefore, it is recommended to adjust the strategy compared to the previous caching methods, such as reducing the cache usage, or leaving such matters to the system when memory warnings are received.

Caching

The purpose of caching data is to reduce the pressure on the CPU, but too much caching can take up too much memory. Due to the memory compression mechanism, there is a trade-off between CPU and memory based on the size of cached data and the cost of recalculating that data.

In some scenarios where you need to cache data, you can consider using NSCache instead of NSDictionary because NSCache cleans up memory automatically and makes more sense when memory is tight.

summary

Normally, we are talking about Dirty Memory and Compressed Memory, Clean Memory does not need to worry too much.

Apps can use more memory space, but the upper limit varies from device to device. The maximum amount of memory you can use with Extension is much lower, so you should be especially careful about memory usage when developing an Extension. When the memory usage exceeds the limit, the system throws EXC_RESOURCE_EXCEPTION.

Tools for analyzing memory usage

Xcode Memory Gauge

In Xcode, you can use the Memory Gauge tool to quickly Gauge the Memory usage of your App when it is running, including the maximum and minimum Memory usage, and the percentage of Memory used by all processes. If you want to see more detailed data, you need Instruments.

Instruments

In Instruments, you can multidimensional analyze your App using Allocations, Leaks, VM Tracker, and Virtual Memory Trace.

Debug Debugger-Memory Resource Exceptions

When you debug with versions prior to Xcode 10, the Debug session will terminate and an exception will be printed on the console if the memory becomes too large. Starting with Xcode 10, the debugger automatically catches EXC_RESOURCE RESOURCE_TYPE_MEMORY exceptions and breaks them where the exception is thrown, making it easy to locate the problem.

Xcode Memory Debugger

With this tool, you can visually view the memory usage of all objects in memory and their dependencies on each other, which is very helpful in locating memory leaks caused by circular references.

You can also go to File->Export Memory Graph to Export it as a memgraph File and use the Developer Tool to analyze it from the command line. This way, you can analyze your App’s memory usage at any time in the past.

Briefly introduce the related commands

Vmmap – View virtual memory

View detailed report

vmmap xx.memgraph

View summary report

vmmap –summary xx.memgraph

View the sum of Ditry Pages for all dynamic libraries with the pipeline command

vmmap -pages xxx.memgraph | grep ‘.dylib’ | awk ‘{sum += $6} END { print “Total Dirty Pages:”sum}’

Only CG image-related data is displayed

vmmap xx.memgraph | grep ‘CG image’

For more information on how to use VMMap, see the documentation

man vmmap

Leaks – View leaked memory

Check for memory leaks

leaks xx.memgraph

Look for a memory leak somewhere

Leaks –traceTree [memory address] xx.memgraph

Check out the Leaks document for more on how to use it

man leaks

Heap – View heap area memory

View the memory usage of all heap objects

heap xx.memgraph

The default is to sort by number of objects, and usually they don’t cause memory problems. We need to be concerned about a small number of objects that consume a large amount of memory, so we can increase the parameter -sortbySize to view the memory usage of all heap objects in the order of memory usage

heap xx.memgraph -sortBySize

After determining which type of object is taking up too much memory, you can get the memory address of each object

heap xx.memgraph -addresses all | ‘XXBigData’

See heap’s documentation for more usage

man heap

Now that we have the memory addresses of these objects, we need another tool to help us do the next step of analysis.

Enabling Malloc Stack Logging

In Product -> Scheme -> Edit Scheme -> Diagnostics, turn on the Malloc Stack function, and the Live Allocations Only option is recommended

The LLDB then logs the stack of objects created during debugging and, with the malloc_history tool, can locate where objects that consume too much memory were created.

Malloc_history – View memory allocation history

malloc_history xx.memgraph [address]

malloc_history xx.memgraph –fullStacks [address]

See the documentation for malloc_history for more usage

man malloc_history

Which tool to choose?

With all the analysis tools mentioned above, which one should we choose? Apple’s engineers helped us do the following:

You can choose according to your needs as shown above.

The picture

On iOS, what kind of data takes up the most memory in most scenarios? Pictures, of course! Note that the amount of memory taken up by images is related to the size of the image, not the file size of the image.

For example, if you have a 590KB image with a resolution of 2048px by 1536px, it doesn’t actually use 590KB of memory, but 2048 * 1536 * 4 = 12 MB.

WWDC 2018 Session 219: WWDC 2018 Session 219: Image and Graphics Best Practices, you can also read the article WWDC2018 Image and Graphics Best Practices

Image format

  • SRGB: This is the current more common full color image gamut, each pixel is 4 bytes

  • Wide: Each pixel is 8 bytes and can represent more colors than sRGB

There are even smaller formats:

  • Brightness and Alpha 8 format: 2 bytes per pixel, monochrome image and Alpha, Metal shader.

  • Alpha 8 format: 1 byte per pixel for monochrome images, 75% smaller than SRGB

Choosing the right format can reduce memory usage. A quick summary:

One byte: Alpha 8 Two bytes: Brightness and Alpha 8 Four bytes: SRGB Eight bytes: WideCopy the code

So the next topic, how to choose the right format?

Choose the correct format

The short answer is: you don’t have to choose the format, you should let the format choose you. Do you feel relieved? Ha ha 😆

Use UIGraphicsImageRenderer UIGraphicsBeginImageContextWithOptions instead

Use UIGraphicsBeginImageContextWithOptions generated images, every pixel need 4 bytes. It is recommended to use UIGraphicsImageRenderer, this method was introduced from iOS 10, iOS 12 automatically selects the best image format and saves a lot of memory.

In addition, if you want to change the color, you can change tintColor directly with no additional memory overhead.

Downsampling

When you shrink an image, multiple pixels will be converted into one pixel by means of average, which is called Downsampling.

When UIImage is set and resized, it needs to compress the original image into memory and then perform a series of transformations to the internal coordinate space, which consumes a lot of resources. We can use ImageIO, which reads the image size and metadata information directly with no additional memory overhead.

In the background, the memory is optimized

Let’s say we show a big image in our App, and when we switch to the background to do something else, that image is still taking up memory. We should consider recycling such data when appropriate.

Demonstrate the Demo

The Demo mainly uses practical examples to illustrate the above knowledge points, which will not be repeated here. Interested children can go to iOS Memory Deep Dive

conclusion

Memory is a limited shared resource, so learn how to use Xcode’s memory analysis tools to understand your application’s memory footprint and use some tips and tricks to reduce your application’s memory footprint.

PS: Have understanding cognition incorrect, welcome to correct!