在 NSMutableArray/NSArray 中保存像素数据;记忆问题

Posted

技术标签:

【中文标题】在 NSMutableArray/NSArray 中保存像素数据;记忆问题【英文标题】:Holding pixel data in NSMutableArray/NSArray; memory issues 【发布时间】:2013-05-06 10:57:38 【问题描述】:

我正在尝试保存有关对象内部像素的基本信息。基本上,(1)像素是否清晰,(2)像素是否在我定义的表面边缘上。我正在使用以下类来定义对象:

@interface PixelInfo : NSObject
@property (nonatomic, assign) BOOL isClear;
@property (nonatomic, assign) BOOL isEdge;
@end

但是,我遇到了内存不足的问题。我正在使用这个对象来跟踪具有可破坏环境的游戏中像素的状态。问题可能是对于 iPad 视网膜大小的图像(2048 x 1536 = 300 万像素),我正在创建 300 万个这样的对象,并且似乎分配了数百 MB 的内存,并导致 iPhone 设备强制退出内存不足的问题。

查看 iPhone 设备内的日志,我得到以下信息:

Name     rpages     recent_max     [reason]             (state)       
MyApp    166400     166400         [per-process-limit]  (frontmost) (resume)

使用 Instruments Allocations 工具,我发现随着时间的推移,数百 MB 的内存被分配给 PixelInfo 对象,直到最终,iPhone 设备强制退出。

基于此,我猜也许我不应该持有这样的像素信息。我也怀疑 Instruments Allocations 显示该设备需要数百 MB 内存才能完成此操作。其他地方可能出了点问题,但我似乎无法确定。

我确实觉得有必要跟踪所有像素的状态。我在破坏环境时使用此像素信息来跟踪图像的状态(即将像素信息isClear 属性设置为 YES,isEdge 属性设置为 NO),然后重新计算新边缘受影响的环境部分。

这是我的主要问题:

    我试图在一个数组中保存 300 万个对象是不是很糟糕? 内存不足显然是一个问题(使用 Instruments Allocations 工具查看),但是这 300 万个对象需要使用数百 MB 的内存是否正确,或者是否有其他原因导致了问题?李>

任何关于我应该如何进一步调试这种情况的想法或指示都会非常有帮助。提前致谢。

-- 只是为了附加上下文(如果相关),这就是我从纹理读取和存储像素信息的方式(使用一些 cocos2d 类):

unsigned char data[4];

// create texture to read pixel data from
CCRenderTexture *renderTexture = [[CCRenderTexture alloc] initWithWidth:widthInPoints
                                                                 height:heightInPoints
                                                            pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[renderTexture beginWithClear:1 g:1 b:1 a:0];

// self.view is a CCSprite object and the visit method draws it to the texture so I can read the pixels
[self.view visit];

for (int i = 0; i < heightInPixels; ++i) 
    for (int j = 0; j < widthInPixels; ++j) 

        // read pixel data
        CGPoint pixelPoint = ccp(j, i);
        glReadPixels(pixelPoint.x, pixelPoint.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data);

        // store pixel data in PixelInfo object; read alpha value in data
        PixelInfo *pixelInfo = [[PixelInfo alloc] init];
        pixelInfo.isClear = [self _isPixelClear:data];

        // add object to _pixelData array (NSMutableArray object)
        [_pixelData addObject:pixelInfo];
        [pixelInfo release];            
    


// release texture
[renderTexture end];
[renderTexture release];

【问题讨论】:

【参考方案1】:

要减少内存使用,请使用两个布尔数组而不是包含具有两个布尔值的对象实例的数组

【讨论】:

您的 PixelInfo 类的一个实例的内存大小优于您的两个布尔值的大小,因此使用两个布尔数组要轻得多并且使用更少的 CPU 查看为每个 PixelInfo 对象分配的内存绝对是问题的核心。当我今晚在家时,我将尝试将实现转换为两个布尔数组。谢谢! 为了进一步减少你的内存使用,你也可以只使用一个 unsigned char 数组并使用掩码来设置/检索你的两个布尔值【参考方案2】:

问题不在于您在 NSArray 中存储了 300 万个 PixelInfo 对象。 (300 万 * (4 + 1 + 1) 字节,假设 NSObject.isa 有 4 个字节,BOOL 有 1 个字节,则只有 18 MB)。 至少从您发布的代码来看,问题似乎在于您只将 PixelInfo 对象添加到 NSArray,但从未释放它们。如果为每个渲染帧添加 300 万个对象到数组中,以 30fps 的速度,您最终可能会获得每秒分配 540MB (18MB * 30) 的内存。您可以看到这如何很快导致您看到的内存问题。在不了解太多细节的情况下,一个改进建议是创建一个短裤数组并更新该数组中的适当值,而不是不断地创建新对象并存储它们。 举例说明:short *pixelInfo = (short *) calloc(1, sizeof(short) * heightInPixels * widthInPixels); // use calloc to zero-fill the bytes...pixelInfo[i*heightInPixels+j] |= [self _isPixelClear: data]; // set isClearpixelInfo[i*heightInPixels+j] |= [self _isPixelEdge: data] &lt;&lt; 1; // set isEdgepixelInfo[i*heightInPixels+j] &amp; 0x1; // isClear set? (pixelInfo[i*heightInPixels+j] &gt;&gt; 1) &amp; 0x1; // isEdge set? 当然,使用这种方法,如果此信息不跨帧持续存在,则需要清除帧之间的像素信息。 如果您能更详细地表达您的需求,可能会提出更具体/更好的建议。

【讨论】:

因为内存对齐,说一个 PixelInfo 实例权重 6 字节太幼稚了,两个 BOOL 很像 8 字节,而且 ARC 不需要放释放指令,我们这样做没有看到数组分配,那么你怎么能假设他在处理它之后没有释放它? 另一件您没有考虑到的事情,对于 PixelInfo 实例,您还必须考虑存储在引用它的数组中的指针,如果是 ARM 上的 4 字节长的指针 谢谢,我从来没有真正考虑过分配对象时的内存开销。这绝对是我实施中的一个主要问题。 为了澄清一点,我在游戏期间保留了这个由 300 万个对象组成的数组。我不再分配了。检查仪器泄漏工具,我认为泄漏不是问题。我正在使用非ARC。我的策略是停止使用我现在正在使用的 PixelInfo 对象并使用更小的存储空间。谢谢!【参考方案3】:

使用位数组。 Michael Dipperstein 的一种这样的实现可以是found here。 C版本包含在Kobold2D和KoboldTouch中。

【讨论】:

感谢您推荐位数组。看来我需要尽可能减少内存占用。由于我不熟悉位数组,因此我将不得不对此进行研究。 还要考虑外部像素。我的意思是在 Retina 上您可以存储 4 个像素的信息,并且您将拥有与非 Retina 上相同的逻辑密度。 感谢您的额外建议。我需要稍微改变我的实现来做到这一点,但它可能更有意义。 我还想向您介绍从对象切换到位数组的最新信息。我最终使用了std::bitset——因为我已经在我的项目中使用了objective-c++(由于使用了Box2D),我认为这是有道理的。使用 Instruments Allocations 工具,内存占用从数百 MB 变为不明显(!)。由于我有 2048*1536 = 3,145,728 像素(iPad 视网膜分辨率)并且我有 2 个布尔值我想为每个像素跟踪,我假设新的内存占用约为 3,145,728 * 2 位 = 768 KB。再次感谢!!

以上是关于在 NSMutableArray/NSArray 中保存像素数据;记忆问题的主要内容,如果未能解决你的问题,请参考以下文章

c# 中 NSMutableDictionary()、NSMutableArray()、NSArray 和 NSDictionary 的等价物是啥?

分配的变量引用在哪里,在堆栈中还是在堆中?

NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记

秋的潇洒在啥?在啥在啥?

上传的数据在云端的怎么查看,保存在啥位置?

在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?