减少峰值内存消耗

时间:2016-01-24 18:14:47

标签: objective-c memory

为了简单起见,我使用下一个简化代码生成位图:

for (int frameIndex = 0; frameIndex < 90; frameIndex++) {

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(130, 130));

    // Making some rendering on the context.

    // Save the current snapshot from the context.
    UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
    [self.snapshots addObject:snapshot];

    UIGraphicsEndImageContext();
}

因此,当操作系统为所有内容提供大约30 MB的内存(在这种特殊情况下,它是观看OS 2,但它不是依赖于操作系统的问题)并且超出配额时,一切都变得非常重要但是一切都变得复杂操作系统只会杀死应用程序的进程。

分配工具的下一个图表说明了问题:

enter image description here

它是相同的图形但具有不同的内存消耗注释 - 在上述代码执行之前,之前和之后。可以看出最终生成了大约5.7 MB的位图,这是绝对可以接受的结果。什么是不可接受的是图形峰值处的内存消耗(44.6 MB) - 所有这些内存都被CoreUI: image data吃掉。鉴于操作发生在后台线程中,执行时间并不重要。

所以问题:什么是减少内存消耗的正确方法(可能通过增加执行时间)以适应内存配额以及为什么尽管UIGraphicsEndImageContext调用内存消耗增加?

更新1: 我认为通过使用NSOperation,NSTimer等分割整个操作将会起到作用,但仍然试图提出一些同步解决方案。

试图收集所有答案并测试下一段代码:

UIGraphicsBeginImageContextWithOptions(CGSizeMake(130, 130));
for (int frameIndex = 0; frameIndex < 45; frameIndex++) {

    // Making some rendering on the context.

    @autoreleasepool {
        UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
        [self.snapshots addObject:snapshot];
    }
    CGContextClearRect(UIGraphicsGetCurrentContext(), CGSizeMake(130, 130));
}

for (int frameIndex = 0; frameIndex < 45; frameIndex++) {

    // Making some rendering on the context.

    @autoreleasepool {
        UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
        [self.snapshots addObject:snapshot];
    }
    CGContextClearRect(UIGraphicsGetCurrentContext(), CGSizeMake(130, 130));
}
UIGraphicsEndImageContext();

发生了什么变化:

  1. 将90次迭代拆分为45个部分。
  2. 将图形上下文移到外部并在每次迭代后清除它而不是创建新的上下文。
  3. 在自动释放池中包装并存储快照。
  4. 结果 - 没有任何改变,内存消耗保持在同一水平。

    此外,如果完全删除并存储快照,它将减少仅4 MB的内存消耗,即小于10%。

    更新2:

    每隔3秒通过计时器进行渲染会生成下一个图表:

    enter image description here

    正如您所看到的那样,即使渲染按时间间隔划分,​​也不会释放内存(确切地说 - 未完全释放)。有些东西告诉我,在执行渲染的对象存在之前,内存不会被释放。

    更新3:

    通过结合3种方法解决了这个问题:

    1. 将整个渲染任务拆分为子任务。例如,90幅图纸被分成6个子任务,每个图纸分为15个图纸(根据经验找到15个)。
    2. 使用dispatch_after连续执行所有子任务,每个子任务后的间隔较小(在我的情况下为0.05秒)。
    3. 最后也是最重要的。为了避免像最后一个图形那样的内存泄漏 - 每个子任务应该在新对象的上下文中执行。例如:

      self.snapshots = [[SnaphotRender new] renderSnapshotsInRange:[0, 15]];

    4. 感谢大家的回答,但@EmilioPelaez最接近正确答案。

4 个答案:

答案 0 :(得分:3)

更正了更新的问题框数。

图像的总字节大小可以是130 * 130 * 4(每像素字节数)* 90 = ~6MB。

不确定手表但临时内存可能正在累积,您可以尝试在@autoreleasepool块中包装快照代码:

@autoreleasepool {
    UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
    [self.snapshots addObject:snapshot];
}

答案 1 :(得分:1)

我认为您的问题是您在同一个上下文中执行所有快照(for循环所在的上下文)。我相信在上下文结束之前,内存不会被释放,这就是图表发生故障时。

我建议你缩小上下文的范围,所以不是使用for循环来绘制所有帧,而是用一些iVars跟踪进度并只画一帧;无论何时完成渲染帧,都可以使用dispatch_after再次调用该函数并修改变量。即使延迟为0,它也会允许上下文结束并清理不再使用的内存。

PS。当我的意思是上下文时,我并不是指图形上下文,我的意思是代码中的某个范围。

答案 2 :(得分:0)

等待,图像分辨率起着重要作用,例如,尺寸为5000 px * 3000 px的一个兆字节jpeg图像将消耗大小为60 MB的RAM,5000 * 3000 * 4个字节为60 MB,图像获得解压缩到ram中,让我们进行故障排除,首先,请告诉我们您使用的是哪种图像尺寸(尺寸)?

答案 3 :(得分:0)

编辑(澄清后):

我认为,正确的方法不应该是直接存储UIImage个对象,而是压缩NSData个对象(即使用 UIImageJPEGRepresentation)。然后,在需要时,将它们再次转换为UIImage个对象。

但是,如果同时使用其中许多内容,则会很快耗尽内存。

原始答案:

实际上,总大小可能高于10MB(可能> 20MB),具体取决于缩放因子(如here所示)。请注意,与UIGraphicsBeginImageContextWithOptions相比,UIGraphicsBeginImageContext需要缩放参数。所以我的猜测是,这与某个截图有关,不是吗?

方法UIGraphicsGetImageFromCurrentImageContext it's thread safe,因此可能会返回副本或使用更多内存。然后你有40MB。

This answer表示iOS以某种方式存储未显示时压缩的图像。这可能是之后6MB使用的原因。

我最后的猜测是设备正在检测内存峰值,然后尝试以某种方式保存内存。由于没有使用图像,因此它会在内部压缩它们并回收内存。

所以我不担心,因为看起来它本身就是在照顾它。但是你可以像其他人建议的那样做,将图像保存到文件中,如果你不打算使用它,就不要把它保存在内存中。

相关问题