iOS上的内存溢出崩溃,但内存消耗只有20MB。

3
我在iPhone 6和5s上使用我的应用程序时遇到了内存不足的崩溃问题。我确定内存是问题所在,因为我收到了内存警告并且有崩溃日志。
我的应用程序中进行视频处理,并在一个后处理步骤中处理大约400个图像,每个图像的资源都会在自动释放池中得到适当释放。崩溃发生在随机重复次数之后。
在这10秒钟内,我分配了大量内存(几百MB),但由于自定义的自动释放池,每个处理过的图像后内存都会被释放。
然而,当我尝试检测问题的根源时,我甚至无法检测到高内存消耗,更不用说搜索问题的来源了。我已经在Instruments的活动监视器中分析了使用的物理内存,无论是我的应用程序还是mediaserverd,在任何给定的时刻都不会消耗超过30 MB的内存。
为什么我的应用程序只使用30MB的内存峰值时会出现内存崩溃?如何正确地测量和调试内存消耗?
非常感谢您提供任何指针!
以下是运行时内存报告的屏幕截图: Memory report 以下是崩溃日志:
Incident Identifier: 2B5E9345-964D-4F6E-87D2-5517F0C22531
CrashReporter Key:   2033c0b344ea7cbf4f153cd862d1e7b68969b42e
Hardware Model:      iPhone6,2
OS Version:          iPhone OS 8.1.1 (12B435)
Kernel Version:      Darwin Kernel Version 14.0.0: Mon Nov  3 22:23:57 PST 2014; root:xnu-2783.3.22~1/RELEASE_ARM64_S5L8960X
Date:                2015-01-26 12:04:35 +0100
Time since snapshot: 672 ms

Free pages:                              2515
Active pages:                            23951
Inactive pages:                          11816
Speculative pages:                       643
Throttled pages:                         0
Purgeable pages:                         0
Wired pages:                             133367
File-backed pages:                       10813
Anonymous pages:                         25597
Compressions:                            8823352
Decompressions:                          752051
Compressor Size:                         83913
Uncompressed Pages in Compressor:        155021
Page Size:                               16384
Largest process:   RedGlam

Processes
Name       |            <UUID>                |     CPU Time|     rpages|       purgeable| recent_max| lifetime_max| fds |  [reason]         | (state)

timed <6fa98ab7f5de312b9bfed47e04e3a43e>         0.075         240                0          +5           701   50   [vm-pageshortage]   (daemon) (idle)
calaccessd <0a7ad7bbfb523bfdbae43aa6f21279f6>         0.134         462                0         +47          1279   50   [vm-pageshortage]   (daemon) (idle)
MobileGestaltHel <19968f31a89230a6b66e51ad668f0921>         0.028         163                0           -           522   50   [vm-pageshortage]   (daemon) (idle)
awdd <58036e1703903ee798a8803de204c300>         0.123         405                0           -          1076   50   [vm-pageshortage]   (daemon) (idle)
WirelessRadioMan <c4181e6d863133e8aa0c95e77a7bb206>         0.051         293                0           -           787   50   [vm-pageshortage]   (daemon) (idle)
notification_pro <b143453e80393938a7ba23a0181dc52c>         0.158         148                0           -           451   50   [vm-pageshortage]   (daemon)
com.apple.dt.ins <c2263ead004b339f9fe48bbdf95286c9>        30.091         255                0           -           787   50   [vm-pageshortage]   (daemon)
debugserver <f00a5e2ac8f73e7e893c711d88c344a6>        24.790         208                0           -           681   50   [vm-pageshortage]   (daemon)
MobileMail <4b48abd990e93dbea47db1cbf328da9e>         6.030        1518                0           -          4299   50                       (resume) (continuous)
lsd <f554bd07b90a3cfc9d9ef9f8e234833c>         1.443         333                0           -           859   50                       (daemon)
tccd <f2878273872231afa1a6e0af2dcb73a6>         0.619         278                0           -           861   50                       (daemon)
MYAPP <417ba797cf3e39df82d79baf41050692>       389.444      105870                0           -         13874   50                       (audio) (frontmost) (resume)
ptpd <a06176d3eefe3e3c8549bb4f6d340658>         1.913         817                0           -          2043   50                       (daemon)
BTServer <2d0fc0974c073a0aafc9954110080950>        16.246         572                0           -          1859   50                       (daemon)
wifid <43e56e539a6a3114bf4cd7646c8dd90e>       149.958         561                0           -          1564   50                       (daemon)
discoveryd <68f73878299336d7872b0ae9ce3f7f08>       179.311         585                0           -          1220  100                       (daemon)
locationd <5b826e2c09c23eaaa2acc2472269cb30>      4461.461        2209                0           -          5025   50                       (daemon)
lockdownd <3a0b3375ad6e391da37a1f79f46843b0>        39.278         364                0           -          1345   50                       (daemon)
syslogd <05f6b5e5512938a892bac5af23ab1c08>       127.902         240                0           -           818   50                       (daemon)
powerd <2b4ae8758a5b3b709a97c452ec08923b>        56.978         310                0           -           579   50                       (daemon)
imagent <d5e037ad2173362d8a6077788b2d7074>        13.323         529                0           -          1354   50                       (daemon)
identityservices <9d4b00e3c6003685ac8697c59f4e4d38>        16.729         687                0           -          1794   50                       (daemon)
vmd <c3b3270d187f3dcaa843ba73f01ff8cb>         0.482         595                0           -          2598   50                       (daemon)
cfprefsd <4325eab208063b998046460a4c2ee484>        32.394         449                0           -           742   50                       (daemon)
iaptransportd <4bf77076d69630e389ba64229c526723>        20.807         364                0           -           971   50                       (daemon)
apsd <bb925404cb1137b09b85671a8d2c7656>        62.713         738                0           -          1892   50                       (daemon)
networkd <fa2acedf0b0035269d66a72e28c3a95a>       307.451         716                0           -          1585   50                       (daemon)
sharingd <1ed17c64831f32ea9cbb47e48c4d222c>        29.442         944                0           -          2298   50                       (daemon)
dataaccessd <33bcaea3bc473f128685f4df14a115eb>        13.535         729                0           -          2230   50                       (daemon)
SCHelper <779250b8a48638958a5922f6ae1066fa>        10.160         134                0           -           324   50                       (daemon)
searchd <e5c5e5675c0935eaab5feb15ebc0b934>         5.392         888                0           -          2986   50                       (daemon)
mediaserverd <a0354e528bc431958df0d50830bead36>      1080.882       91747                0           -         21944  200                       (daemon)
syslog_relay <087c67d0371b324fb6df48442016ec90>         6.746         114                0           -           237   50                       (daemon)
SpringBoard <96f929dd23123d8bbc9ba2a0bb48bde1>      1108.457        8031                0           -         17537   50
backboardd <e263837653b434f1880f9d37b3926998>      7219.545        7865                0           -          4676   50                       (daemon)
UserEventAgent <f5a211b9c88e3fa481f2bd1ee1f5a921>      1084.316        1000                0           -          2811  100                       (daemon)
fseventsd <16c9b62bb28c388ca10d54dbff18c4f8>        20.426         496                0           -           843   50                       (daemon)
configd <ed40fcde35ae337ab3b70073199564b1>       117.075         537                0           -          1463   50                       (daemon)
fairplayd.H2 <cae337642f6d396b82ac54e72bc0e0a4>         6.550         151                0           -          1433   50                       (daemon)
wirelessproxd <ab1fa7e43a7c3f9393533404c2cc80b8>         1.814         264                0           -           986   50                       (daemon)
assertiond <10ec04add18f3ecd8a8efbb1cc4e2bd6>        18.292         325                0           -          1045   50                       (daemon)
aggregated <281958649a3130aab6ecb1aa47f0a6c1>      2273.629        1367                0           -          2822   50                       (daemon)
distnoted <cb5e76091dc53ceeaf65290f8e197a89>         4.937         207                0           -           335   50                       (daemon)
discoveryd_helpe <492c39ae2d643adca0ed971675c77406>         0.120         156                0           -           752   50                       (daemon)
filecoordination <5ec159db1afe3317878b8ab794e2d7d1>         1.259         308                0           -           941   50                       (daemon)
DTMobileIS <2fdc94aa5069338e815fbe3a13e3d95c>       551.592        2538                0           -         36844   50                       (daemon)
gputoolsd <97e54c888a9a3978a85fd965a71e7669>         9.503        1031                0           -          2910   50                       (daemon)
CommCenter <33412ab229c738c8860c70803fed173b>       787.039        1465                0           -          4737   50                       (daemon)
notifyd <5fa8fd5e44c83f64be1475b882b16c82>       142.939         384                0           -           441   50                       (daemon)
ReportCrash <e946799f25f833fd9b37a6a1c7b1993c>         0.039         166                0           -           551   50                       (daemon)

**End**

这是我正在使用的其中一个效果的代码(renderAnimation2DOverFace不分配任何内存):
- (CIImage *) render:(CIImage*)targetImage imageContext:(CIContext*) imageContext
      facialFeatures:(NSArray*)facialFeatures currentFrameInd:(int)frameInd
{
    if ( !facialFeatures ) return targetImage;

    @autoreleasepool {
        UIImage *editingImage = [[UIImage alloc] initWithCIImage:targetImage];
        UIGraphicsBeginImageContextWithOptions(editingImage.size, NO, 1.0);

        [editingImage drawInRect:CGRectMake( 0, 0, editingImage.size.width, editingImage.size.height)];

        for ( ArtechFacialFeature *facialFeature in facialFeatures ) {
            [self renderAnimation2DOverFace:facialFeature.bounds
                                            currentFrameInd:frameInd];
        }

        UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        targetImage = [targetImage initWithCGImage:resultImage.CGImage options:nil];

        editingImage = nil;
        resultImage = nil;
    }

    return targetImage;
}

这是另一种视频效果例程:
- (CIImage *) render:(CIImage*)targetImage imageContext:(CIContext*) imageContext
    facialFeatures:(NSArray*)facialFeatures currentFrameInd:(int)frameInd
{
    if ( !facialFeatures ) return targetImage;

    @autoreleasepool {
        CGImageRef inputImage = [imageContext createCGImage:targetImage fromRect:[targetImage extent]];

        GPUImagePicture *sourcePicture = [[GPUImagePicture alloc] initWithCGImage:inputImage];
        GPUImageOutput *currentOutput = [self createFilterChain:sourcePicture facialFeatures:facialFeatures
            imageExtent:targetImage.extent];

        [currentOutput useNextFrameForImageCapture];
        [sourcePicture processImage];

        UIImage *currentFilteredVideoFrame = [currentOutput imageFromCurrentFramebuffer];

        targetImage = [targetImage initWithCGImage:currentFilteredVideoFrame.CGImage];

        currentFilteredVideoFrame = nil;

        [sourcePicture removeAllTargets];
        sourcePicture = nil;

        [currentOutput removeOutputFramebuffer];
        currentOutput = nil;

        CFRelease( inputImage );

        [[GPUImageContext sharedFramebufferCache] purgeAllUnassignedFramebuffers];
    }

    return targetImage;
}

你在分析过程中是否收到了内存警告?你也应该检查是否有内存泄漏。 - Cihan Tek
Zaph,是的,我可以在10秒内处理大约400张图像。每个帧的资源都会在自动释放池中得到适当的释放。崩溃发生在随机重复次数之后。我会将这些信息添加到问题中,谢谢! - Senad
请添加带有“自定义自动释放池”的代码? - zaph
你确定你没有因为某个“wild”大小值而发出一个巨大的请求吗? - Hot Licks
你尝试过创建另一个指向CIImage的指针,而不是目标图像,将其作为返回图像。在autorelease池块中调用retain,然后使用autorelease返回它吗?理论上ARC应该保留对targetimage的retain直到它释放,但我不知道是否会发生NSZombie。 - Naughty_Ottsel
显示剩余3条评论
2个回答

4

在循环中进行使用自动释放内存的处理时,需要在循环内部添加自动释放池。请注意,许多Cocoa和Foundation方法都使用自动释放内存。

问题在于每个操作都会保留和自动释放内存,但是内存实际上要等到当前自动释放池被清空才会被释放,通常在运行循环中进行,但由于循环过紧,这个过程并没有被调用。

示例:

for (...) {
    @autoreleasepool {
        perform work
    }
}

您可以将自动释放池放在操作较小的部分周围。
为了调试,降低循环次数以避免崩溃,然后使用Heapshot:
使用Instruments检查泄漏和由于保留但未泄漏的内存导致的内存损失。后者是指仍然指向的未使用内存。在Instruments中的Allocations工具上使用Mark Generation (Heapshot)检查。
如何使用Heapshot找到内存增长,请参见:bbum博客
基本方法是运行Instruments allocate工具,拍摄一个堆快照,运行您的代码的一次迭代,再拍摄一个堆快照,重复3或4次。这将表明在迭代期间分配但未释放的内存。
为了找出结果,公开查看每个分配。
如果需要查看对象的保留、释放和自动释放发生的位置,请使用Instruments:
在Instruments上运行,在Allocations中打开“记录引用计数”(对于Xcode 5及以下版本,您必须停止记录才能设置该选项)。让应用程序运行,停止录制,深入挖掘并可以查看所有保留、释放和自动释放发生的位置。

这就是我实现该程序的方式。如果我将autoreleasepool注释掉,内存消耗峰值会达到约80MB。此外,该应用程序在每次调用效果时都会崩溃。目前,崩溃情况是不规律和随机的。 - Senad
我一直在使用Allocations工具对分配进行分析。但是,我犯了一个错误:我将快照放置在执行视频效果之间,这意味着只有在执行完整个循环后才能看到结果。此时,“外部”释放池已经释放了所有内存。 - Senad

0
问题在于有一个CIImage在autorelease池之外和渲染方法之外被取消引用,这个我已经在问题中发布了。
传递的参数是targetImage。现在我在方法调用之外添加了另一个autorelease池,解决了所有内存问题。
然而,对我来说仍然是个谜,为什么那些未释放的CIImage分配不会在内存监视器和其他工具中表现出使用的内存量。
此外,我不明白为什么崩溃只会偶尔发生。

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