iOS应用最大内存预算


152

我正在开发一款至少定位3gs的ios游戏。我们将高清资产用于视网膜显示设备(iphone 4,ipod touch 4th gen)。

在内存方面,Ipod Touch 4th gen似乎对我们来说是最受限制的设备,因为它具有与3gs相同的RAM(与Iphone 4的512相比,为256),但是我们使用的是HD资产。尝试加载100-110mb的ram时,该应用程序曾经崩溃过,但是现在我们的内存降到了70MB,我们再也没有加载过崩溃了。

经过大量搜索之后,似乎没有官方的硬限制,因此我们应该如何知道使用什么内存预算是安全的呢?我们希望能够给艺术家一个预算,使他们可以在没有记忆的情况下使用每张地图。



14
不确定此问题如何与以后提出的问题重复。
贾斯珀(Jasper)2016年

Answers:


42

我认为您已经回答了自己的问题:请尽量不要超过70 Mb的限制,但这实际上取决于很多事情:您使用的是哪个iOS版本(不是SDK),在后台运行的应用程序数量,确切的内存是多少您正在使用等

只是要避免立即的内存飞溅(例如,您使用40 Mb的RAM,然后为一些简短的计算分配80 Mb的内存)。在这种情况下,iOS会立即终止您的应用程序。

您还应该考虑延迟加载资产(仅在确实需要时加载而不是在事先加载)。


2
只是我们想尽可能多地放一些东西(图形和声音)。艺术家总是希望尽可能多地投入游戏中,这就是为什么我要限制他们的预算。我猜我们只需要在不同设置下的许多不同设备上进行测试,以找到合理的最大内存占用空间即可使用。
frilla 2011年

2
是否会随时(即使在其他需要大量内存的应用程序中大量使用之后)在该设备上仅分配70MB(可能在预算之内)始终保证成功分配,还是可能仍然崩溃?
史蒂文·卢

1
@Steven Lu取决于您的设备。例如,在较新的设备上,例如iPhone5或iPad4,70 Mb的分配完全不是问题。
2013年

1
是的,但是我想知道是否可以确保只要我的应用程序总使用率保持在神奇的设备专用内存预算范围内,它就不会终止!
史蒂文·卢

1
没有保证
最大

421

使用实用程序Split编写的测试结果(链接在他的答案中):

设备:(崩溃数量/总数/占总数的百分比)

  • iPad1:127MB / 256MB / 49%
  • iPad2:275MB / 512MB / 53%
  • iPad3:645MB / 1024MB / 62%
  • iPad4:585MB / 1024MB / 57%(iOS 8.1)
  • 第一代iPad Mini:297MB / 512MB / 58%
  • iPad Mini视网膜: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:130MB / 256MB / 51%(iOS 6.1.1)
  • 第五代iPod touch: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)

2
iPhone4:已确认类似的价值,似乎合法:P
cprcrack

3
iPhone 5的崩溃速度为±645 MB。
asp_net 2013年

4
@JasperPol我已编辑您的帖子,以包含我拥有的各种设备,我希望可以。我已经添加了我测试过的iOS版本,以防万一,但如果您认为它不重要,请随时将其删除。
JosephH 2014年

2
很棒,此列表已创建并维护。以我的经验,为了安全起见,我不得不将内存保持低得多,可能是此处所示的20%。设备之间的差异也很大。
user1021430

1
只需在12.9 iPad Pro上运行即可。内存警告为2451MB,崩溃为3064MB,总计3981MB。

134

我创建了一个小实用程序,该程序尝试分配尽可能多的内存以使其崩溃,并记录何时发生内存警告和崩溃。这有助于找出任何iOS设备的内存预算是多少。

https://github.com/Split82/iOSMemoryBudgetTest


我做了一个有趣的测试:使用监视内存使用情况的xcode运行我的应用程序,进入后台,运行BudgetTest。测试没有完成,而我的后台应用却没有。我有兴趣知道为什么。另外,这与@cprcrack在其他答案中所说的相反。
罗伯托

19

在我的应用程序中,如果使用更多的内存,则用户体验会更好,因此,我必须确定是否真的应该释放所有可以使用的内存didReceiveMemoryWarning。根据Split和Jasper Pol的回答,最多使用总设备内存的45%似乎是一个安全的门槛(谢谢大家)。

如果有人想看看我的实际实现:

#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(基于此答案):

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
}

1
大小应为TASK_BASIC_INFO_COUNT而不是sizeof(info)-此错误已复制粘贴到许多具有相同代码的地方
Maxim Kholyavkin 2013年

谢谢Speakus。根据此链接,您似乎是正确的。您还有其他参考资料可以找到吗?
cprcrack

苹果使用TASK_BASIC_INFO_COUNT
马克西姆Kholyavkin

45%不再是安全的限制,对于iPhone X,它太接近50%的崩溃值。我建议为每个设备使用40%或单独的值。
Slyv

8

通过分叉SPLITS存储库,我构建了一个模块来测试可分配给Today's Extension的iOS内存

iOSMemoryBudgetTestForExtension

以下是我在iPhone 5s中得到的结果

内存警告为10 MB

应用程序崩溃了12 MB

通过这种方式,苹果只是允许任何扩展发挥其全部潜力


7

您应该从WWDC 2010会议视频中观看会议147 。它是“ iPhone OS上的高级性能优化,第2部分”。
关于内存优化有很多好的建议。

一些技巧是:

  • 使用nested NSAutoReleasePools确保您的内存使用率不会增加。
  • CGImageSource从大图像创建缩略图时使用。
  • 响应内存不足警告。

我的问题不是关于如何优化(尽管感谢链接),而是我们可以允许自己使用多少。原因是,例如,如果我们优化增益为20mb,那么如果它在合理的“预算”之内,那么艺术家将希望使用该20mb,也要确保它不会引起任何性能问题或内存崩溃。
frilla 2011年

好。崩溃的原因是由于内存不足,操作系统正在终止应用程序。你可以只添加一个NSLog内部的didReceiveMemoryWarning,然后做一些测试,你分配不同数量的内存,然后看到当内存不足警告开始一命呜呼
Kobski

4

从iOS13开始,有一种Apple支持的方式,可以使用

#include <os/proc.h>

size_t os_proc_available_memory(void)

在这里介绍:https//developer.apple.com/videos/play/wwdc2019/606/

大约29分钟左右。

编辑:添加到文档的链接 https://developer.apple.com/documentation/os/3191911-os_proc_available_memory?language=objc


最后!我在少数设备上测试了os_proc_available_memory(),结果与上面的大表中的值非常相似!
Slyv

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;
}

如果使用TASK_BASIC_INFO_COUNT而不是MACH_TASK_BASIC_INFO,您将获得

kerr == KERN_INVALID_ARGUMENT(4)


您至少应该提到您的答案几乎是上述@cprcrack的精确复制和粘贴。唯一的区别是TASK_BASIC_INFO_COUNT。
mrvincenzo

2

通过按设备RAM对Jaspers列表进行排序,我又创建了一个列表(我使用Split的工具进行了自己的测试,并修复了一些结果-在Jaspers线程中检查我的评论)。

设备RAM:崩溃的百分比范围

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

特别案例:

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

设备RAM可以轻松读取:

[NSProcessInfo processInfo].physicalMemory

根据我的经验,可以安全地将45%用于1GB设备,50%用于2 / 3GB设备和55%用于4GB设备。macOS的百分比可能更大。


更新:看来iPhone X是个例外-当使用50%的RAM时崩溃(通过iOSMemoryBudgetTest应用测试)。我更新了清单。
Slyv '18年

0

使用上面的许多答案,我实现os_proc_available_memory()了iOS 13+的Apples新方法,NSByteCountFormatter并提供了许多有用的格式化选项,以实现更好的内存输出:

#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]);
    }
}

重要说明:不要忘()了最后。NSLogmemoryLoggingOutput方法中包括了这两个选项,因为它不会警告您它们丢失了,并且如果不包含方括号,则会返回意外且恒定的结果。

从该方法返回的字符串memoryStringForBytes输出如下所示的值:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.