iOS Memory Allocation - How Much Memory Can Be Used in an Application

iOS memory allocation - how much memory can be used in an application?

Blocks from separate memory allocations are not allocated contiguously (separate calls to alloc, malloc, new, etc.). Otherwise they are allocated contiguously(from the same call to malloc, ex. new float[30]). According to Apple your app risks being shut down for memory usage when you use more than 20mb of ram. In practice however, you can get to about...

  • 260 MB of ram on iPad 2 (Thanks RobCroll)
  • 170-180MB of ram on devices with 512 Mb of ram total (iPhone 4, iPod touch 4g)
  • 40-80MB of ram on devices that have 256 MB of ram (iPad, iPhone 3gs, iPod touch 3g)
  • 25 MB on device with only 128MB of ram (IPhone 3g, iPhone 2g, iPod touch 1g-2g)

If you really "need" that much ram for a mobile application, you should really save the data to a temp file and do your processing on that. An easy way to do that is by using memory mapped files.

How much memory can one iOS app use?

I wrote a test app that measures how much memory an app can allocate before it's killed. Here are the numbers:

  • iPhone 5s (iOS 10, debug mode, 1GB memory): 600MB can be allocated
  • iPad Air 2 (iOS 11.4, 2GB memory): 1.3GB can be allocated
  • iPhone X (iOS 11.4, 3GB memory): 1.2GB can be allocated
  • iPhone 7 Plus (iOS 12.1, 3GB memory): 1.8GB can be allocated
  • iPad 13-inch (iOS 11.4, 4GB memory): 3GB can be allocated

It's interesting that I never got a memory warning.

Here's the code if you want to run the test yourself:

import UIKit

let sizeInMb = 100

class Wrapper {
var array = [UInt8](repeating: 0, count: sizeInMb * 1048576) // 100 MB
}

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)

var i = 0

sleep(5) // So that you can see how much memory it consumes before any allocations.

while true {
let w = Wrapper()
Unmanaged.passRetained(w)
i += 1
print("\(i * sizeInMb) MB allocated")
sleep(1) // Give the OS a chance to kill other processes.
}

return true
}

func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
print("Memory warning!")
}
}

This does not work on the simulator. Anything regarding performance should be tested on device.

ios app maximum memory budget

I think you've answered your own question: try not to go beyond the 70 Mb limit, however it really depends on many things: what iOS version you're using (not SDK), how many applications running in background, what exact memory you're using etc.

Just avoid the instant memory splashes (e.g. you're using 40 Mb of RAM, and then allocating 80 Mb's more for some short computation). In this case iOS would kill your application immediately.

You should also consider lazy loading of assets (load them only when you really need and not beforehand).

IOS App memory usage standards

Welcome to SO!

No, that doesn't strike me as much memory usage. Take an empty new project (Single View template), for example. That already takes up about 6 MB. Whether it's in background or foreground.
IIRC the iPhone 6 has 1 GB of actual RAM, but the way the architecture works makes the memory less an impact on performance on e.g. desktop systems. With only 7 MB of using that your app doesn't even need to be swapped unless the user has literally dozens of other, more hungry apps running.

Basically, you're hardly above the minimum that an app will consume just for displaying a white screen...

Ideal memory usage amounts for iOS apps

You could get(a pretty nice) overview from this SO question. It won't show you low-medium values, but if you know the limit you can adjust below limit.

If you are near the limits in some view - override didReceiveMemoryWarning and dispose resources accordingly.

My advise is to test always on device, as simulator need a lot memory just because of it's architecture and it's not relative to real devices.

What is the limit on virtual memory for iOS?

tl;dr:

There are no direct or explicit virtual memory limits enforced by the OS, so your app could in theory allocate its entire potential logical address space. This size is is 4 gigabytes for 32 bit processors, and 18 exabytes for 64 bit processors.

Practically speaking, there are limitations in available virtual memory size which depend heavily on how your application uses its memory space. These limitations stem from inefficiencies in the way the OS manages virtual memory, as the OS is tuned aggressively for low power use, not heavy file I/O.

If you don't care about the finer details of these practical limits, feel free to stop reading here. Otherwise, what follows are my notes from researching a memory issue on iOS mixed with a primer on virtual memory and memory mapped files, and plenty of discussion on what makes iOS different from other OSes.


What's a Virtual Memory?

In modern operating systems, including iOS, all memory your process can access is technically virtual memory. It's called "virtual" memory because the address space exposed to the process (called the logical address space of the process) does not necessarily align with the physical address space of the machine, or even the virtual address space of other processes. Virtual memory allows for the kernel to provide different logical address spaces in different contexts.

On iOS, the size of this logical address space is in theory only limited by the pointer size, which is defined by the processor architecture. This means process has roughly 4 gigabytes of logical memory space on 32 bit processors, and 18 exabytes on 64 bit processors.

But my device only has 2GiB of RAM, how can I actually use 18 exabytes of memory space?

To answer this question, first you have to realize that when your process calls something like malloc to get a bit of memory, the kernel sets a side a bit of your process's logical address space and marks it allocated. In this case, this allocated memory also occupies physical memory space. Data stored in physical memory is called resident, in that it resides in physical memory, while the data which is resident is said to be part of the resident set. The kernel generally counts your physical memory usage against physical memory limits by tracking the resident set size for your process.

If this was the only way your process is allocating memory, then for most modern operating systems your process would be limited by the amount of unallocated physical memory, plus the amount of free space available in the machine's page file, swap partition, or some other nonvolatile backing store. However, iOS has no backing store and it enforces fairly strict constraints on the maximum resident set size of a process, so generally your app's resident set size will be limited by the lesser of the available physical memory and the per process resident set limit.

So if allocating memory doesn't ever let you approach the limit, why bother having 18 exabytes of address space per process?

One example of where this is useful is memory mapped files.

When you have an app that needs fast access to files and you want to rely on the kernel to figure out which bits of the file to read in and when, you can memory map the file (see documentation for mmap). In this case, the kernel presents the file (or the portion of the file you've requested) to your process as a contiguous region of the logical address space of the process.

How do memory mapped files work?

The OS will trap on accesses to the portion of the logical memory space reserved for the mapped file, and transparently copy chunks of your file into physical memory as they are accessed. These "chunks" are called pages, and the trap which allows the kernel to do this is called a page fault, while the act of making data resident is called paging in, and the removal of pages from memory is called paging out. This all makes it seem to your process like you have the region of the file which you have mapped (or the entire file, if that's what you mapped) in memory all the time. Of course, in order for this to work well, the OS must detect pages which haven't been accessed in a while, and page them out.

How does iOS mapped file support differ from other operating systems?

In the above scenario, iOS is a bit limited compared to most desktop class operating systems. Specifically, from what I can see in the documentation, it would appear that on iOS pages are only able purged from physical memory when they have been mapped read only, using the PROT_READ flag. This means that if you map a file with the PROT_WRITE flag, pages from this file will stay resident until you call munmap (I couldn't tell from the docs alone whether this means all pages will remain resident, or if it's just modified pages which stick around).

This behaviour deviates from other operating systems, as in the case of PROT_WRITE with MAP_SHARED, unmodified pages can be purged at any time, and modified pages may be purged once they are synchronized with the disk (aka, once they are no longer "dirty"). Also with other operating systems, when using PROT_WRITE and MAP_PRIVATE the case of unmodified pages remains the same, but modified pages can still be purged from physical memory so long as the OS is using some "backing store," such as a page file.

In the latter case, iOS can't page out these modifications as it has no backing store. I find the prior case confusing however, as in theory there's no logical limitation to allow synchronized pages to be paged out. The most I can infer is that in the PROT_WRITE and MAP_SHARED case, these pages are kept resident to avoid the need for tight synchronization between the I/O scheduler and pager in an SMP environment. This inference is supported by the weak suggestion in the documentation (link below) that on OSX, modified pages would be written to the backing store, regardless of whether or not they are synchronized with the disk. I intend to do a bit more reading on the Mach kernel to more fully understand these limitations, however.

So mmap with PROT_READ appears to be our path to using all of our process's available VM space without hitting resident set size limits, right? I mean, with this method you could in theory create read-only mappings of some large file many times over, with each mapping occupying a separate portion of your process's available logical memory space. On a 32bit OS, even on 32bit iOS, you can likely achieve your poorly-thought-out plans of world domination by virtual address space saturation quite easily. ENOMEM for everyone!

So if there aren't any virtual memory limits, why does my app get killed when I map read only files?

Practically speaking, while on many operating systems you can just mmap a file and rest easy while the OS figures out the details, this isn't really the case on iOS. Even if read-only mapped files dominate the logical address space of the process it's still possible that your app can be killed for going over resident set memory limits. This happens because of the way the kernel decides which pages can be removed from physical memory.

When reading this next part, remember that iOS has been tuned very aggressively for power efficiency. It's not a file server.

The kernel only frees pages which have been marked inactive, and I would infer from the docs linked below that pages are only able to be marked inactive if they have not been touched for some threshold period of time.

I would also infer from the docs and from my own experience that the piece of the kernel which is responsible for marking pages inactive does not coordinate well with the piece of the kernel which is responsible for checking resident set size limits.

As your resident set size approaches limits, the OS attempts to clear inactive pages, and fires off memory warnings, to which your app is expected to respond by reducing its resident set size. Once your resident set size crosses a certain threshold, jetsam kills your app.

So, given the above, what happens if the piece of the kernel which is responsible for marking pages inactive hasn't run in a while when your app is doing high-volume random access over a large number of pages?

Your app gets killed, even when the bulk of your resident set is occupied by pages of read-only mapped files.

I believe this is one of the primary reasons why apple recommends using smaller, more transient mappings where possible, instead of mapping the entire file (although in that usage pattern, memory space fragmentation starts to become a concern).

This answer wasn't ridiculously long enough! I demand more info!

If you'd like to know more, please see Apple's document on virtual memory in iOS and OSX, as well as the memory mapped files section of the File System Advanced Programming Topics doc, and the mmap man page for iOS.

As an aside, there are also some other related topics worth reading up on which make this all a bunch more complex and useful, such as page locking, page synchronization, I/O caching, prefetching, and for the deeply curious, how the OS accomplishes efficient multiprocessing access to shared mappings.

How much memory does iOS allow apps to use?

The answer is: as much as you want, until the OS complains.

There is no such thing as an absolute memory limit, since it wholly depends on the phone's memory, how much the OS and its related services are taking up, and the time of the month (just kidding, but you get the idea).

You should be scaling down these large image files to fit into memory, or even more so, not loading them all at once considering the screen is so small and only so much can be viewed at any one particular time.

How much memory can an iPhone app take up for iPhone 4

That should be fine. There aren't any "recommended limits", per se, although you may find the answers to this question helpful: ios app maximum memory budget.



Related Topics



Leave a reply



Submit