我在这里有一个很普遍的问题。 一般来说,你会发现谁会记住你?
我有一个视频编码器,设置非常复杂,images
进入控制器,编码器在另一个,我要求images
并通过代表获取它们有时会通过多级控制器,我也在这个过程中使用了一些dispatch_async
次调用。图像是UIView
的快照,并使用CoreGraphics
处理,我保留最终图像并在使用后将其释放到另一个控制器中。一切正常,内存大约持续25Mb,但是在我完成编码后,内存速度非常快,最多一分钟从25Mb到330Mb,当然是崩溃。我试图放入日志,看看是否仍然要求图像,但似乎没有任何问题,编码器按预期停止。编码器设置为在后台运行。
一个重要的事情是,如果我试图找到泄漏(或分配,因为泄漏没有报告ARC的任何内容),应用程序会更快崩溃,但不是因为内存。我怀疑我以某种方式搞砸了调度,并且由于仪器引起的某些延迟在指定时间无法获得。但是,如果没有日志,我也会遇到麻烦。我在用乐器调试时能看到日志吗?
感谢任何有用的信息。
编辑:我成功运行带有allocs的仪器而没有做任何事情,似乎崩溃不一致。我保存了仪器报告,你可以看到内存是如何上升的,有一个导致这种情况的分配,我认为这个问题会重新开始。该文件位于http://ge.tt/1PF97Pj/v/0?c
答案 0 :(得分:2)
这里的问题是你在adaptor.assetWriterInput.readyForMoreMediaData
上“忙着等待”,即在一个紧凑的循环中反复调用它。一般来说,这是不好的做法。标题声明此属性是Key-Value Observable,因此您最好重新构建代码以侦听键值更改通知,以便推进整个过程。更糟糕的是,取决于AVAssetInputWriter
的工作原理(我不确定它是否基于运行循环),这里忙碌等待的行为实际上可能阻止资产输入编写者做任何实际的工作,因为运行循环可能会有效地死锁,等待工作完成,直到你让运行循环继续进行才可能发生。
现在你可能会问自己:忙碌如何导致内存压力?它导致内存压力,因为在幕后,readyForMoreMediaData
导致每次调用它时都会分配自动释放的对象。因为你忙于等待这个值,在一个紧凑的循环中反复检查它,它只是分配越来越多的对象,它们永远不会被释放,因为运行循环永远不会有机会为你弹出自动释放池。 (有关分配实际用途的更多详细信息,请参见下文)如果您想继续这种(不明智的)忙碌等待,可以通过执行以下操作来缓解您的内存问题:
BOOL ready = NO;
do {
@autoreleasepool {
ready = adaptor.assetWriterInput.readyForMoreMediaData;
}
} while (!ready);
这将导致在每次检查后释放由readyForMoreMediaData
创建的任何自动释放的对象。但实际上,从长远来看,通过重组此代码以避免忙碌等待,您将获得更好的服务。如果你绝对必须忙碌等待,至少在循环的每次传递中做usleep(500);
之类的事情,所以你不要把CPU 作为进行多次颠簸。但是不要忙着等。
编辑:我也看到你想了解如何从乐器中解决这个问题。让我试着解释一下。从您发布的文件开始,这就是我所做的:
我点击了顶部窗格中的Allocations行 然后我选择了“Created& Still Living”选项(因为如果事情被破坏,我们就不会看到堆增长。) 接下来,我通过Option-拖动您看到的大“斜坡”中的小范围来应用时间过滤器。 此时,窗口如下所示:
在这里,我看到列表中有很多非常相似的4K malloc'ed对象。这是吸烟枪。 现在我选择其中一个,然后展开窗口的右窗格以显示堆栈跟踪。 此时,窗口如下所示:
在右侧面板中,我们看到正在创建该对象的堆栈跟踪,我们看到它在AVAssetInputWriter
中被分配,但是下面的第一个函数(在视觉上方)代码中的最后一帧是-[AVAssetWriterInput isReadForMoreMediaData]
。回溯中的autorelease
提示这与自动释放的对象有关,并且坐在这样的紧密循环中,标准自动释放机制永远不会有机会运行(即弹出当前池)。
我从这个堆栈得出的结论是-[AVAssetWriterInput isReadForMoreMediaData]
中的某些东西(可能是下一个堆栈帧中的_helper
函数)在返回结果之前会执行[[foo retain] autorelease]
。自动释放机制需要跟踪自动释放的所有内容,直到弹出/排出自动释放池。为了跟踪这些,它需要为其“等待自动释放的事物列表”分配空间。这是我猜测为什么这些是malloc块而不是自动释放的对象。 (即没有分配任何对象,而只是空间来跟踪自推出池以来发生的所有自动释放操作 - 其中有很多因为您正在检查这个属性是一个紧密的循环。)
这就是我诊断出这个问题的方法。希望这将在未来帮助你。
答案 1 :(得分:0)
要回答我的问题,如果我删除dispatch_async
电话,内存问题就会解决,但是现在我的用户界面被阻止了,这根本就不好。它应该是一种结合所有这些的方法,所以我不会阻止它。这是我的代码
- (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)];
//});
}
}