174

I'm working on an ios game that's targeting as a minimum the 3gs. We are using HD assets for retina display devices (iphone 4, ipod touch 4th gen).

Memory wise, Ipod Touch 4th gen seems to be the most constraint device for us since it has the same amount of RAM (256 compared to Iphone 4's 512) as 3gs but we're using HD assets on it. The app used to crash when trying to load 100-110mb of ram but now that we're down to 70MB, we've never had loading crash.

After lots of searching around, there seems to be no official hard limit so how should we go about knowing what memory budget to use to be safe? We want to be able to give the artists a budget they can use without memory worries for each map.

frilla
  • 1,773
  • 3
  • 11
  • 8
  • 1
    Possible duplicate of [iOS memory allocation - how much memory can be used in an application?](http://stackoverflow.com/questions/6044147/ios-memory-allocation-how-much-memory-can-be-used-in-an-application) – nhgrif Nov 30 '15 at 22:11
  • 18
    Not sure how this question can be a duplicate of something which was asked at a later point of time. – Jasper Jan 07 '16 at 10:48

11 Answers11

475

Results of testing with the utility Split wrote (link is in his answer):

device: (crash amount/total amount/percentage of total)

  • iPad1: 127MB/256MB/49%
  • iPad2: 275MB/512MB/53%
  • iPad3: 645MB/1024MB/62%
  • iPad4: 585MB/1024MB/57% (iOS 8.1)
  • iPad Mini 1st Generation: 297MB/512MB/58%
  • iPad Mini retina: 696MB/1024MB/68% (iOS 7.1)
  • iPad Air: 697MB/1024MB/68%
  • iPad Air 2: 1383MB/2048MB/68% (iOS 10.2.1)
  • iPad Pro 9.7": 1395MB/1971MB/71% (iOS 10.0.2 (14A456))
  • iPad Pro 10.5”: 3057/4000/76% (iOS 11 beta4)
  • iPad Pro 12.9” (2015): 3058/3999/76% (iOS 11.2.1)
  • iPad Pro 12.9” (2017): 3057/3974/77% (iOS 11 beta4)
  • iPad Pro 11.0” (2018): 2858/3769/76% (iOS 12.1)
  • iPad Pro 12.9” (2018, 1TB): 4598/5650/81% (iOS 12.1)
  • iPad 10.2: 1844/2998/62% (iOS 13.2.3)
  • iPod touch 4th gen: 130MB/256MB/51% (iOS 6.1.1)
  • iPod touch 5th gen: 286MB/512MB/56% (iOS 7.0)
  • iPhone4: 325MB/512MB/63%
  • iPhone4s: 286MB/512MB/56%
  • iPhone5: 645MB/1024MB/62%
  • iPhone5s: 646MB/1024MB/63%
  • iPhone6: 645MB/1024MB/62% (iOS 8.x)
  • iPhone6+: 645MB/1024MB/62% (iOS 8.x)
  • iPhone6s: 1396MB/2048MB/68% (iOS 9.2)
  • iPhone6s+: 1392MB/2048MB/68% (iOS 10.2.1)
  • iPhoneSE: 1395MB/2048MB/69% (iOS 9.3)
  • iPhone7: 1395/2048MB/68% (iOS 10.2)
  • iPhone7+: 2040MB/3072MB/66% (iOS 10.2.1)
  • iPhone8: 1364/1990MB/70% (iOS 12.1)
  • iPhone X: 1392/2785/50% (iOS 11.2.1)
  • iPhone XS: 2040/3754/54% (iOS 12.1)
  • iPhone XS Max: 2039/3735/55% (iOS 12.1)
  • iPhone XR: 1792/2813/63% (iOS 12.1)
  • iPhone 11: 2068/3844/54% (iOS 13.1.3)
  • iPhone 11 Pro Max: 2067/3740/55% (iOS 13.2.3)
  • iPhone 12 Pro: 3054/5703/54% (iOS 16.1)
kolyuchiy
  • 5,465
  • 2
  • 23
  • 31
Jasper
  • 7,031
  • 3
  • 35
  • 43
  • 2
    iPhone4: similar value confirmed, seems legit :P – cprcrack Oct 30 '13 at 19:53
  • By the way, this app is also VERY useful to test state restoration in a real device, because it causes other apps to be freed from memory. – cprcrack Nov 01 '13 at 11:21
  • 3
    iPhone 5 crashes at ±645 MB. – asp_net Dec 15 '13 at 21:03
  • iPhone 5S crashes at ±646 MB pretty reliably here. – eAi Oct 03 '14 at 13:30
  • There is an iPad 3rd generation and an iPad 4th generation. iPad3 refers to the 3rd generation I assume? If so, what are 4th generation specs? (See http://support.apple.com/en-us/HT5452 for device types.) – rizzes Nov 10 '14 at 19:35
  • I don't have an iPad 4th generation available for testing. If you find out, let me know and I'll add it to the list. – Jasper Nov 11 '14 at 09:14
  • 4
    @JasperPol I've edited your post to include various devices I have, I hope that's okay. I've added the iOS version I tested on in case it matters, but feel free to remove it if you think it's not important. – JosephH Nov 18 '14 at 16:32
  • iPhone 4S crashes at ±286MB (286MB/512MB/56%). – Xaree Lee May 29 '15 at 21:50
  • iPhone 4S doesn't crash until it reaches ±363 MB for me. (iOS 5.1.1) – Soeholm Jun 03 '15 at 16:53
  • 2
    Awesome that this list has been created and maintained. In my experience, I've had to keep memory much lower to be safe, maybe 20% of what's shown here. Device-to-device differences are also highly variable. – jeremywhuff Aug 10 '15 at 19:24
  • 1
    Just ran this on a 12.9 iPad Pro. Memory warning at 2451MB, crash at 3064MB, total 3981MB. – lock Jul 15 '16 at 13:22
  • iPhone 6s+: 1392MB/2048MB/ 68% (iOS 10.2.1); iPhone 7+: 2040MB/3072MB/66% (iOS 10.2.1) – Slyv Feb 13 '17 at 15:39
  • I retested iPad Air 2, because current values on list looks suspiciously (only 58% on 2GB device). And indeed my result is similar to other 2gb devices: iPad Air 2: 1383MB/2048MB/68% (iOS 10.2.1) – Slyv Feb 14 '17 at 19:19
  • And the result for the last missing device: iPhone 7: 1395/2048MB/68% (iOS 10.2) – Slyv Feb 14 '17 at 21:58
  • sO You have all these ios devices on standby or you use simulator?? – Lightsout Jul 21 '17 at 04:13
  • 1
    @bakalolo its not possible to use simulator to test, as it would use your mac it's ram – Jasper Jul 27 '17 at 15:46
  • @Jasper: Please add 2 new devices: iPad Pro 10.5”: 3057/4000/76% (iOS 11 beta4) iPad Pro 12.9” (2017): 3057/3974/77% (iOS 11 beta4) – Slyv Aug 08 '17 at 08:21
  • The amount is a resident size (RAM) used by my app or does the GPU resources are counted too? – Joey.Z Sep 12 '17 at 13:25
  • @zoujyjs GPU is not taken into account. – Jasper Nov 17 '17 at 20:36
  • @Jasper: I tested 2 more devices: iPhone X: 1392/2785/50% (iOS 11.2.1) !!! ; iPad Pro 12.9” (2015): 3058/3999/76% (iOS 11.2.1) ; The important thing is iPhone X crashes at only 1392MB, which is only 50% of total memory. I tested it 2 times to make sure. 2nd time was just after killing all apps and rebooting. Results were the same. – Slyv Jan 18 '18 at 11:39
  • @Jasper No, in my case (a game app) GPU resources is accounted. – Joey.Z Jun 25 '18 at 13:52
  • @zoujyjs I'm not sure, you'll have to check as I'm a bit out of the development world.. What I meant is that I don't think the tool Split wrote is taking GPU into account – Jasper Jun 26 '18 at 14:51
  • I have just made tests on iOS 12 - iPhone X and iPad Pro 12.9” (2017). Results are very similar to iOS 11. – Slyv Sep 20 '18 at 08:43
  • @Jasper Data for new devices from 2018: iPhone XS Max: 2039/3735/55% (iOS 12.1); iPad Pro 11.0” 256GB (2018): 2858/3769/76% (iOS 12.1); iPad Pro 12.9” 1TB (2018): 4598/5650/81% (iOS 12.1); – Slyv Nov 20 '18 at 11:23
  • @Jasper Two more results: iPhone8: 1364/1990MB/70% (iOS 12.1) iPhone XS: 2040/3754/54% (iOS 12.1) Jasper you can change device name from 'iPad Pro 12.9” (2018)' to 'iPad Pro 12.9” (2018, 1TB)'. This is important because only 1TB version has 6GB Ram, others have 4GB. Anybody with iPhone XR or iPad 12.9 (2018, 4GB)? – Slyv Nov 30 '18 at 08:24
  • 1
    @Slyv, here you go for iPhone XR: 1792/2813/63% (iOS 12.1) – jbernardo Feb 07 '19 at 16:39
  • @Jasper Could you update the list with few recent comments (iPhone XR, iPhone 8, iPhone XS)? – Slyv Mar 12 '19 at 11:42
  • 1
    @Jasper Here are reports from 3 more devices: iPhone 11 Pro Max: 2067/3740/55% (iOS 13.2.3); iPhone 11: 2068/3844/54% (iOS 13.1.3); iPad 10.2: 1844/2998/62% (iOS 13.2.3); Please update the list – Slyv Nov 25 '19 at 09:06
  • iPhone 11 (4GB) and iPhone XR (3GB) crash at 1.8g~2g , iPhone X (3GB) and iPhone 8 (2GB) crash at ~1.4g...seems apple reserved about 1GB memory for these devices with dual or more cameras – Dikey Jul 30 '20 at 09:36
  • Xcode 14.1 shows memory limit in Debug Navigator Memory graph when memory usage approaches it. – kolyuchiy Nov 07 '22 at 09:51
144

I created small utility which tries to allocate as much memory as possible to crash and it records when memory warnings and crash happened. This helps to find out what's the memory budget for any iOS device.

https://github.com/Split82/iOSMemoryBudgetTest

Split
  • 4,319
  • 4
  • 19
  • 10
  • I made an interesting test: ran my app with xcode monitoring memory usage, entered background, ran the BudgetTest. The test was killed while my in-background app wasn't. I'm interested in knowing why. Also, this goes against what @cprcrack said in the other answer. – Roberto May 18 '17 at 04:24
47

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).

Max
  • 16,679
  • 4
  • 44
  • 57
  • 2
    It's just that we wanted to put as much stuff as we could (graphics & sounds). Artists will always want to put as much as they possibly can into a game that's why I want to limit them with a budget. I guess we'll just have to test on many different devices in different settings to find a reasonable maximum memory footprint to use. – frilla May 05 '11 at 00:42
  • 2
    Will allocating only 70MB (which is presumably under the budget) at *any* time on that device (even after heavy usage in other memory-hungry apps) always guarantee a successful allocation, or will it potentially still crash? – Steven Lu Sep 03 '13 at 23:29
  • 1
    @Steven Lu it depends on your device. E.g. on newer ones, like iPhone5 or iPad4 70 Mb allocation is not a problem at all. – Max Sep 04 '13 at 09:55
  • 1
    yes but i want to know if I can be sure that as long as I keep my app's total usage under the magical device specific memory budget that it will not get terminated! – Steven Lu Sep 04 '13 at 16:52
  • 2
    there are no guarantees – Max Sep 04 '13 at 18:49
21

In my app, user experience is better if more memory is used, so I have to decide if I really should free all the memory I can in didReceiveMemoryWarning. Based on Split's and Jasper Pol's answer, using a maximum of 45% of the total device memory appears to be a safe threshold (thanks guys).

In case someone wants to look at my actual implementation:

#import "mach/mach.h"

- (void)didReceiveMemoryWarning
{
    // Remember to call super
    [super didReceiveMemoryWarning];

    // If we are using more than 45% of the memory, free even important resources,
    // because the app might be killed by the OS if we don't
    if ([self __getMemoryUsedPer1] > 0.45)
    {
        // Free important resources here
    }

    // Free regular unimportant resources always here
}

- (float)__getMemoryUsedPer1
{
    struct mach_task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
    if (kerr == KERN_SUCCESS)
    {
        float used_bytes = info.resident_size;
        float total_bytes = [NSProcessInfo processInfo].physicalMemory;
        //NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
        return used_bytes / total_bytes;
    }
    return 1;
}

Swift (based on this answer):

func __getMemoryUsedPer1() -> Float
{
    let MACH_TASK_BASIC_INFO_COUNT = (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))
    let name = mach_task_self_
    let flavor = task_flavor_t(MACH_TASK_BASIC_INFO)
    var size = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)
    var infoPointer = UnsafeMutablePointer<mach_task_basic_info>.alloc(1)
    let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)
    let info = infoPointer.move()
    infoPointer.dealloc(1)
    if kerr == KERN_SUCCESS
    {
        var used_bytes: Float = Float(info.resident_size)
        var total_bytes: Float = Float(NSProcessInfo.processInfo().physicalMemory)
        println("Used: \(used_bytes / 1024.0 / 1024.0) MB out of \(total_bytes / 1024.0 / 1024.0) MB (\(used_bytes * 100.0 / total_bytes)%%)")
        return used_bytes / total_bytes
    }
    return 1
}
Community
  • 1
  • 1
cprcrack
  • 17,118
  • 7
  • 88
  • 91
  • 1
    size should be TASK_BASIC_INFO_COUNT instead of sizeof(info) - this mistake copy-pasted to many places with same code – Maxim Kholyavkin Dec 02 '13 at 01:13
  • Thanks Speakus. You seem to be right based on [this link](http://www.gnu.org/software/hurd/gnumach-doc/Task-Information.html). Do you have any other reference where this information can be found? – cprcrack Dec 02 '13 at 23:21
  • apple uses TASK_BASIC_INFO_COUNT [too](http://www.opensource.apple.com/source/xnu/xnu-1699.22.73/bsd/kern/vm_pressure.c) – Maxim Kholyavkin Dec 03 '13 at 03:12
  • 45% is not safe limit anymore, it is too close to 50% crash value for iPhone X. I suggest using 40%, or separate value for each device. – Slyv Nov 20 '18 at 11:46
9

By forking SPLITS repo, I built one to test iOS memory that can be allocated to the Today's Extension

iOSMemoryBudgetTestForExtension

Following is the result that i got in iPhone 5s

Memory Warning at 10 MB

App Crashed at 12 MB

By this means Apple is merely allowing any extensions to work with their full potential.

Jasper
  • 7,031
  • 3
  • 35
  • 43
Harsh
  • 2,852
  • 1
  • 13
  • 27
8

You should watch session 147 from the WWDC 2010 Session videos. It is "Advanced Performance Optimization on iPhone OS, part 2".
There is a lot of good advice on memory optimizations.

Some of the tips are:

  • Use nested NSAutoReleasePools to make sure your memory usage does not spike.
  • Use CGImageSource when creating thumbnails from large images.
  • Respond to low memory warnings.
Kobski
  • 1,636
  • 15
  • 27
  • My question is not about how to optimize (thanks for the link though), it's about how much can we allow ourselves to use. The reason is that for example, if we optimize to gain 20mb, then artists will want to use that 20mb if it's within the reasonable "budget", aka sure that it won't cause any performance issues or memory crash. – frilla May 05 '11 at 00:20
  • OK. The crash will be because the OS is terminating the app because of constrained memory. You could just add an `NSLog` inside `didReceiveMemoryWarning` and then do some testing where you allocate different amounts of memory and then see when the memory warnings start to kick in. – Kobski May 05 '11 at 06:13
8

Starting with iOS13, there is an Apple-supported way of querying this by using

#include <os/proc.h>

size_t os_proc_available_memory(void)

Introduced here: https://developer.apple.com/videos/play/wwdc2019/606/

Around min 29-ish.

Edit: Adding link to documentation https://developer.apple.com/documentation/os/3191911-os_proc_available_memory?language=objc

  • Finally! I tested os_proc_available_memory() on few devices, and results are very similar to the values i the big table above! – Slyv Jan 24 '20 at 14:42
4

I created one more list by sorting Jaspers list by device RAM (I made my own tests with Split's tool and fixed some results - check my comments in Jaspers thread).

device RAM: percent range to crash

  • 256MB: 49% - 51%
  • 512MB: 53% - 63%
  • 1024MB: 57% - 68%
  • 2048MB: 68% - 69%
  • 3072MB: 63% - 66%
  • 4096MB: 77%
  • 6144MB: 81%

Special cases:

  • iPhone X (3072MB): 50%
  • iPhone XS/XS Max (4096MB): 55%
  • iPhone XR (3072MB): 63%
  • iPhone 11/11 Pro Max (4096MB): 54% - 55%

Device RAM can be read easily:

[NSProcessInfo processInfo].physicalMemory

From my experience it is safe to use 45% for 1GB devices, 50% for 2/3GB devices and 55% for 4GB devices. Percent for macOS can be a bit bigger.

Slyv
  • 421
  • 3
  • 8
  • update: It seems iPhone X is an exception - it crashes when 50% of RAM is used (tested with iOSMemoryBudgetTest app). I updated the list. – Slyv Jan 18 '18 at 11:25
3
- (float)__getMemoryUsedPer1
{
    struct mach_task_basic_info info;
    mach_msg_type_number_t size = MACH_TASK_BASIC_INFO;
    kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
    if (kerr == KERN_SUCCESS)
    {
        float used_bytes = info.resident_size;
        float total_bytes = [NSProcessInfo processInfo].physicalMemory;
        //NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
        return used_bytes / total_bytes;
    }
    return 1;
}

If one will use TASK_BASIC_INFO_COUNT instead of MACH_TASK_BASIC_INFO, you will get

kerr == KERN_INVALID_ARGUMENT (4)

  • You should at least mention that your answer is almost an exact copy & paste of **@cprcrack's** above. The only difference is TASK_BASIC_INFO_COUNT. – tonymontana Feb 25 '20 at 16:48
1

Working with the many answers above, I have implemented Apples new method os_proc_available_memory() for iOS 13+ coupled with NSByteCountFormatter which offers a number of useful formatting options for nicer output of the memory:

#include <os/proc.h>

....

- (NSString *)memoryStringForBytes:(unsigned long long)memoryBytes {
    NSByteCountFormatter *byteFormatter = [[NSByteCountFormatter alloc] init];
    byteFormatter.allowedUnits = NSByteCountFormatterUseGB;
    byteFormatter.countStyle = NSByteCountFormatterCountStyleMemory;
    NSString *memoryString = [byteFormatter stringFromByteCount:memoryBytes];
    return memoryString;
}

- (void)memoryLoggingOutput {
    if (@available(iOS 13.0, *)) {
        NSLog(@"Physical memory available: %@", [self memoryStringForBytes:[NSProcessInfo processInfo].physicalMemory]);
        NSLog(@"Memory A (brackets): %@", [self memoryStringForBytes:(long)os_proc_available_memory()]);
        NSLog(@"Memory B (no brackets): %@", [self memoryStringForBytes:(long)os_proc_available_memory]);
    }
}

Important note: Do not forget the () at the end. I have included both NSLog options in in the memoryLoggingOutput method because it does not warn you that they are missing and failure to include the brackets returns an unexpected yet constant result.

The string returned from the method memoryStringForBytes outputs values like so:

NSLog(@"%@", [self memoryStringForBytes:(long)os_proc_available_memory()]); // 1.93 GB
// 2 seconds later
NSLog(@"%@", [self memoryStringForBytes:(long)os_proc_available_memory()]); // 1.84 GB
App Dev Guy
  • 5,396
  • 4
  • 31
  • 54
0

It is not only about memory % usage, but also about memory fragmentation. If your app allocates and deallocates big chunks mixed with smaller chunks it might be, that big chunks wont fit into the reserved memory for the app and your app will get "jetsammed" even if you havent used a lot of your memory (e.g. at 35% of the total amount)

If that is the case, you should consider using memory pools and reuse of objects. Also a heavy use of String-Operations might be bad for your memory food print.

hhamm
  • 1,511
  • 15
  • 22