如何避免iOS中的堆碎片问题

8
我们的应用程序在运行时会创建大量的小对象。这主要是由于Autoreleased NSString和NSNumber对象引起的。由于该应用程序被设计为在后台运行“24/7”,因此堆碎片化成为一个重要问题。
有哪些技术可以避免完全重构程序而又能解决这个问题呢?
我想到了一些方法: - 对象池:在最后释放后将对象返回到池中,但是需要将对象设为可变的(那么NSMuttableString本身会导致堆碎片吗?) 其他人如何处理这些问题呢?
编辑:这就是我怀疑内存碎片化的原因。请查看rpages和[vm-pageshortage]。
    eIncident Identifier: 81E87769-8E16-4439-AFFA-6D077E01E5ED
CrashReporter Key:   96235931c31c6b92a16f5c1b1e4cb363a3d18a67
Hardware Model:      iPhone4,1
OS Version:          iPhone OS 7.0.4 (11B554a)
Kernel Version:      Darwin Kernel Version 14.0.0: Fri Sep 27 23:00:48 PDT 2013; root:xnu-2423.3.12~1/RELEASE_ARM_S5L8940X
Date:                2013-12-13 22:43:36 -0800
Time since snapshot: 1582 ms

Free pages:                              1105
Active pages:                            3668
Inactive pages:                          2035
Speculative pages:                       46
Throttled pages:                         100120
Purgeable pages:                         0
Wired pages:                             22159
File-backed pages:                       5400
Anonymous pages:                         349
Compressions:                            0
Decompressions:                          0
Compressor Size:                         0
Uncompressed Pages in Compressor:        0
Largest process:   Argus

Processes
     Name                    <UUID>                       rpages       recent_max   fds      [reason]          (state)

        Facebook <979b9707d85a31df94b986d91d8c3ce7>         2368             2368  100   [vm-pageshortage]  (resume)
       MobileSMS <339505ebbbc4301e87379b095a38ba13>         1448             1448  100   [vm-pageshortage]  (background)
      MobileMail <b3574f4bded1315cb2e50e5de205be48>         1575             1575  100   [vm-pageshortage]  (resume) (continuous)
            tccd <1fea8c5a71943151b5cd304c7eb0fd8c>          198              198  100   [vm-pageshortage]  (daemon)
             kbd <be2d64e41bf43e48a09a23fb129eb0b4>          739              739  100   [vm-pageshortage]  (daemon)
      librariand <15fb21b24e823e158caed9f9e9d8b87a>          299              299  100   [vm-pageshortage]  (daemon)
     MobilePhone <10e2242652423ae28f278a807a0d6384>         1852             1852  200   [vm-pageshortage]  (continuous)
       CVMServer <f26614f7fef63e2faa518272f0fc600a>           96               96  200   [vm-pageshortage]  (daemon)
           Argus <d214b453a3453121a8495d5c8eba80fd>        51299            51299  100   [vm-pageshortage]  (location) (frontmost) (resume)
identityservices <18cc20db2e4739a782cc8e38e03eff52>          398              398  100                      (daemon)
           wifid <a5cf99e5a0f032a69bc2f65050b44291>          652              652   25                      (daemon)
         syslogd <6539f4cf4dcf34daadf1d99991926680>          140              140   50                      (daemon)
          powerd <0a253ac2a99236809422214be1700bc0>          126              126  100                      (daemon)
             vmd <93cffd22b64631afa08a42f6a85e1f33>          297              297  100                      (daemon)
         imagent <bef102e1faef39209926fb25f428a71e>          438              438  100                      (daemon)

3
好的,以下是翻译的结果:我只是好奇:你是如何检测/测量堆碎片化的?它究竟造成了什么问题? - Martin R
1
你打算如何控制这些类的内部内存分配? - Till
当iOS内存不足时,它会关闭我们的应用程序和其他一些应用程序。我们确实处理了低内存警告并清除了大部分内存,使其降至约10Mb左右,因此这不是问题所在。 - pkuhar
在iOS7中,未知进程的崩溃日志开始出现,并列出了因[vm-pageshortage]而被终止的应用程序列表。 - pkuhar
我认为这是iOS7中已知的缺陷。许多“24/7”应用程序似乎受到影响。Safari经常导致此崩溃。我们的应用程序也是如此,主要在5S上出现。你无法做太多事情,因为(至少在我们的情况下)我们的内存使用限制很高。通常在1GB设备上使用200MB。 - Steven Kramer
Steven Kramer,即使在收到内存警告后仅使用11MB的内存时,我们仍然遇到了这个问题。非常令人沮丧。 - pkuhar
1个回答

4
解决这个问题的一种方法是找到导致碎片化问题的“90%”罪魁祸首。您可能在代码中有一种病态条件,会导致瑞士奶酪效应。
初步操作
首先要做的是确信您的高页面使用率是由碎片化引起的,而不是内存泄漏。如果您还没有这样做,使用Xcode中的“Instruments”应用程序是观察iOS模拟器中程序分配内存的绝佳方式。使用“Allocations”和“Leaks”工具可以记录代码执行的每个对象分配和malloc(),以及方便的时间戳。(并且免费提供的“Leaks”工具会显示对象映射中的循环,如果ARC没有像应该那样释放内存,则会出现这种情况。)通常,这些工具会跟踪仍在使用的内存。您可以在“分配”配置窗格中选择“已创建和已销毁”选项,以跟踪所有内容,可以在概述信息正上方的弹出窗口中查看。还有一个VM页面分配工具,可能会阐明您的问题。
可能的补救措施
一旦确定了罪魁祸首,就可以重新构造代码以防止病态条件的发生。或者,如果重构成本太高,可以通过重用内存来缓解条件的影响。通常情况下,当分析显示对象分配和释放导致出现空洞或者特定对象被频繁地分配/释放时,我会将罪魁祸首变成一个对象“池”。这可以只是将对象存储在NSMutableArray中,并根据需要进行推入和弹出。当然,您可能需要更复杂一些的东西,它可以在获取时初始化对象,在池中准备好对象并在其为空或低时自动填充对象。在iOS中,如果收集了太多对象或接收到低内存警告,则还要修剪池。好处是,这可以是非常通用的代码,您可以永远重复使用。:-)
附注
您提到不想使对象可变的问题。在某些情况下(比如NSString),如果您知道字符串的空间要求和要执行的操作,它可能有助于提高效率。这样,您可以事先告诉它需要多少空间,并在原地操作字符串,从而减少分配开销。(NSString在幕后所做的是另一篇文章。:-)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接