【问题标题】:ios memory going up very fastios内存增长非常快
【发布时间】:2013-06-10 22:26:22
【问题描述】:

我有一个非常笼统的问题。 一般来说,你会怎么做才能找到谁在夺走你的记忆?

我有一个视频编码器,设置非常复杂,images 在一个控制器中,而编码器在另一个控制器中,我要求images 并通过有时要经过很多次的代表来获取它们控制器级别,我还在此过程中使用了一些dispatch_async 调用。图像是UIView 的快照并使用CoreGraphics 处理,我将保留最终图像并在使用后将其释放到另一个控制器中。一切正常,内存一直在 25Mb 左右,但是在我完成编码后,内存增长非常快,最多一分钟从 25Mb 变为 330Mb,当然会崩溃。我尝试放置日志并查看是否仍在要求图像但似乎没有任何问题,编码器按预期停止。编码器设置为在后台运行。

一件重要的事情是,如果我尝试查找泄漏(或分配,因为泄漏没有使用 ARC 报告任何内容),应用程序会更快崩溃,但不是因为内存。我怀疑我以某种方式弄乱了调度,并且由于仪器造成的一些延迟,某些东西在指定的时间不可用。但是,我也很难在没有日志的情况下找到它。使用仪器调试时可以看到日志吗?

感谢您提供任何有用的信息。

编辑:我没有做任何事情就成功地使用 allocs 运行仪器,似乎崩溃不一致。我保存了仪器报告,你可以看到内存是如何增加的,有一个分配导致了这种情况,我认为问题又回到了如何阅读它。文件在这里http://ge.tt/1PF97Pj/v/0?c

【问题讨论】:

  • 查看 Console.app,它显示了您系统的日志。在仪器下运行时您可能看不到日志,但它们会转到 Console.app。

标签: ios objective-c memory instruments


【解决方案1】:

为了回答我的问题,如果我删除了 dispatch_async 调用,内存问题就得到了解决,但是现在我的 UI 被阻止了,这一点都不好。这应该是一种结合所有这些的方法,所以我不会阻止它。这是我的代码

- (void) image:(CGImageRef)cgimage atFrameTime:(CMTime)frameTime {
//NSLog(@"> ExporterController image");
NSLog(@"ExporterController image atFrameTime %lli", frameTime.value);

if (!self.isInBackground && frameTime.value % 20 == 0) {
    dispatch_async(dispatch_get_main_queue(),^{
        //logo.imageView.image = [UIImage imageWithCGImage:cgimage];
        statusLabel.text = [NSString stringWithFormat:@"%i%%", frameCount/**100/self.videoMaximumFrames*/];
    });
}

if (cgimage == nil || prepareForCancel) {
    NSLog(@"FINALIZE THE VIDEO PREMATURELY cgimage == nil or prepareForCancel is YES");
    [self finalizeVideo];
    [logo stop];
    return;
}

// Add the image to the video file
//dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),^{
    NSLog(@"ExporterController buffer");
    CVPixelBufferRef buffer = [self pixelBufferFromCGImage:cgimage andSize:videoSize];
    NSLog(@"ExporterController buffer ok");
    BOOL append_ok = NO;
    int j = 0;

    while (!append_ok && j < 30) {

        if (adaptor.assetWriterInput.readyForMoreMediaData) {
            //printf("appending framecount %d, %lld %d\n", frameCount, frameTime.value, frameTime.timescale);
            append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];
            if (buffer) CVBufferRelease(buffer);
            while (!adaptor.assetWriterInput.readyForMoreMediaData) {}
        }
        else {
            printf("adaptor not ready %d, %d\n", frameCount, j);
            //[NSThread sleepForTimeInterval:0.1];
            while(!adaptor.assetWriterInput.readyForMoreMediaData) {}
        }
        j++;
    }
    if (!append_ok) {
        printf("error appending image %d times %d\n", frameCount, j);
    }
NSLog(@"ExporterController cgimage alive");
    CGImageRelease(cgimage);
NSLog(@"ExporterController cgimage released");
//});


frameCount++;
if (frameCount > 100) {
    NSLog(@"FINALIZING VIDEO");
    //dispatch_async(dispatch_get_main_queue(),^{
        [self finalizeVideo];
    //});
}
else {
    NSLog(@"ExporterController prepare for next one");
    //dispatch_async(dispatch_get_main_queue(),^{
    //dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),^{
        NSLog(@"ExporterController requesting next image");
        [self.slideshowDelegate requestImageForFrameTime:CMTimeMake (frameCount, (int32_t)kRecordingFPS)];
    //});
}

}

【讨论】:

  • 为了进一步回答我的问题,我成功地通过将 requestImageForFrameTime 包装在 dispatch_async(dispatch_get_main_queue(),^{ 中来解除对 UI 的阻塞。以前我有太多的调度程序使这个无法工作。
【解决方案2】:

这里的问题是您在adaptor.assetWriterInput.readyForMoreMediaData 上“忙于等待”,即在一个紧密的循环中一遍又一遍地调用它。一般来说,这是不好的做法。标头声明此属性是 Key-Value Observable,因此您最好重组代码以侦听 Key-Value 更改通知,以推进整个过程。更糟糕的是,取决于AVAssetInputWriter 的工作方式(我不确定它是否基于运行循环),这里的忙等待行为实际上可能阻止资产输入编写器执行任何操作真正的工作,因为运行循环可能会有效地死锁,等待完成的工作可能不会发生,直到你让运行循环继续。

现在您可能会问自己:忙等待是如何造成内存压力的?它会导致内存压力,因为在幕后,readyForMoreMediaData 会导致每次调用它时都会分配自动释放的对象。因为你忙着等待这个值,在一个紧密的循环中一遍又一遍地检查它,它只会分配越来越多的对象,它们永远不会被释放,因为运行循环永远没有机会为你弹出自动释放池。 (有关分配真正用途的更多详细信息,请参见下文)如果您想继续这种(不明智的)忙等待,您可以通过执行以下操作来缓解内存问题:

BOOL ready = NO;
do {
    @autoreleasepool {
        ready = adaptor.assetWriterInput.readyForMoreMediaData;
    }
} while (!ready);

这将导致readyForMoreMediaData 创建的任何自动释放对象在每次检查后被释放。但实际上,从长远来看,通过重组此代码以避免忙碌等待,您会得到更好的服务。如果您绝对必须忙着等待,至少在循环的每次通过时都执行usleep(500); 之类的操作,这样您就不会过多地消耗 CPU。但不要忙着等待。

编辑:我还看到您想了解如何从 Instruments 中解决这个问题。让我试着解释一下。从您发布的文件开始,这就是我所做的:

我点击了顶部窗格中的分配行 然后我选择了“Created & Still Living”选项(因为如果东西被破坏了,我们就不会看到堆增长。) 接下来,我通过选项在您看到的大“斜坡”中拖动一个小范围来应用时间过滤器。 此时,窗口如下所示:

在这里,我看到列表中有大量非常相似的 4K malloc'ed 对象。这是吸烟枪。 现在我选择其中一个,然后展开窗口的右窗格以显示堆栈跟踪。 此时,窗口如下所示:

在右侧面板中,我们看到创建该对象的堆栈跟踪,并且我们看到它在AVAssetInputWriter 中被向下分配,但下面的第一个函数(视觉上方)代码中的最后一帧是@ 987654330@。回溯中的autorelease 暗示这与自动释放的对象有关,并且像这样处于紧密循环中,标准的自动释放机制永远没有机会运行(即弹出当前池)。

我从这个堆栈得出的结论是-[AVAssetWriterInput isReadForMoreMediaData] 中的某些东西(可能是下一个堆栈帧中的_helper 函数)在返回结果之前执行了[[foo retain] autorelease]。自动释放机制需要跟踪所有被自动释放的东西,直到自动释放池被弹出/耗尽。为了跟踪这些,它需要为其“等待自动释放的事物列表”分配空间。这就是我对为什么这些是 malloc 块而不是自动释放对象的猜测。 (即没有分配任何 objects,而只是空间来跟踪自池被推送以来发生的所有自动释放操作 - 其中有很多,因为您正在检查这个属性在一个紧密的循环中。)

这就是我诊断问题的方式。希望这对您将来有所帮助。

【讨论】:

  • 谢谢。似乎使用 kvo 我只收到一次有关更改的通知,也许在那之后没有更改?主线程确实被这个紧密的循环阻塞了,所以我现在要做的是延迟请求下一张图像。这个问题很少,cpu有很多尖峰,我觉得不好看,时间会更长,而且UI被波浪挡住了。我有一个旋转的物体来显示进度。使用 dispatch_async 没有波澜,只是移动得更慢。内存问题来自我的dyspatch_async组合,但如何正确使用它们是另一个讨论。
  • 标题非常明确地说“这个属性是可观察的键值。观察者不应该假设他们会被通知特定线程的更改。”因此,如果您没有收到通知,那么我敢打赌它不会改变。但是,如果您希望通知到达主线程,则需要自己编组它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-20
  • 2016-02-24
  • 2014-05-03
  • 2016-08-23
  • 1970-01-01
  • 1970-01-01
  • 2013-10-08
相关资源
最近更新 更多