1

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

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

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

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

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

4

2 回答 2

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 中解决这个问题。让我试着解释一下。从您发布的文件开始,这就是我所做的:

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

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

在右侧面板中,我们看到了创建该对象的堆栈跟踪,我们看到它被分配AVAssetInputWriter-[AVAssetWriterInput isReadForMoreMediaData]. 在autorelease回溯中有一个提示,这与自动释放的对象有关,并且像这样处于紧密循环中,标准的自动释放机制永远没有机会运行(即弹出当前池)。

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

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

于 2013-06-14T12:14:27.250 回答
0

为了回答我的问题,如果我删除呼叫,内存问题就得到了解决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)];
    //});
}

}

于 2013-06-14T11:17:16.493 回答