gpt4 book ai didi

cocoa - 加载和释放NSImage时内存继续增加

转载 作者:行者123 更新时间:2023-12-03 16:39:03 25 4
gpt4 key购买 nike

我有一个问题,我的应用程序在连续加载图像文件时,将内存消耗到“崩溃点”。例如,考虑以下代码,该代码反复加载和释放15MB JPEG文件(用于测试目的的大文件):

NSURL *inputUrl = [NSURL URLWithString:@"file:///Users/me/Desktop/15MBjpeg.jpg"];  
for(int i=0; i<1000; i++) {
NSImage *image = [[NSImage alloc] initWithContentsOfURL:inputUrl];
[image release];
}


由于有大量可用的系统内存,因此它在前几个应用程序中的执行速度很快,但最终系统在我所说的“崩溃点”崩溃了。在这里,我相信系统会释放足够的内存来加载下一个图像,因此性能最终会变慢。此外,现在其他应用程序运行缓慢,因为系统必须释放这些被占用但现在尚未使用的内存。

对我来说有意义的是,如果它将分配内存,然后让系统释放它,以使我在Activity Monitor中的“ Real Mem”统计数据保持较小,而不是在连续的加载/释放迭代中达到千兆字节。实际上,我可能永远不会到此为止,但是当随时需要的实际驻留内存通常很小时,我的活动监视器“ Real Mem”统计信息最终会超过所有其他应用程序,这似乎很奇怪。我原本以为这是某种内存泄漏或缓存问题,但它似乎与应用程序中的主动内存分配和系统上的懒惰内存释放有关(不是,如果操作系统具有该策略,就不会有任何错误-如果那是实际上是这样的)。也许我虽然完全错过了一些东西。

有没有更好的方法可以重复加载图像,而又不会浪费时间并且不会主动缓解内存使用行为?也许有一种方法可以减少应用程序的内存占用量,或者有一种方法可以使图像如何加载到相同的对象或内存位置中而变得更聪明?我的目标是加载图像,对其进行处理(获取缩略图,更改图像格式等),在内存中删除它,然后再次执行-所有这些都不会导致观察到的内存增长。

-

跟进:

巴伐利亚,谢谢。包装的NSAutoreleasePool确实可以解决加载相同文件时的迭代内存增长:

NSURL *inputUrl = [NSURL URLWithString:@"file:///Users/me/Desktop/15MBjpeg.jpg"];  
for(int i=0; i<1000; i++) {
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
NSImage *image = [[NSImage alloc] initWithContentsOfURL:inputUrl];
[image release];
[apool drain];
}


但是,它无法解决释放映像后内存保持增加(并且耗尽NSAutoreleasePool)的问题。例如,当加载我的15MB JPEG图像时,“ Real Mem”内存从8MB稳定状态跳到大约25MB,然后停留在那里。 (我的应用程序只有一个Interface Builder按钮,该按钮具有一个有线IBAction到仅调用我复制的for循环的方法)。我希望在for循环完成之后(或者如果仅加载和释放一个映像),“ Real Mem”统计信息将减少回名义上的应用程序级别内存使用率。

在调用NSImage功能时也可以在后台加载其他内容,这似乎是合理的,这会增加内存。但是,不同大小的图像(15MB,30MB,50MB等)会按比例增加应用程序中的内存,这使我相信它不仅仅是这种分配。

此外,如果我尝试连续加载单独的图像(例如15MBjpeg-1.jpg,15MBjpeg-2.jpg等),则内存有时会随每个加载的新图像复合。例如,如果连续加载两个映像,则根据我的观察,加载/释放后,应用程序的“ Real Mem”内存使用量现在约为50MB,并且永远不会减少。当加载后续图像时,此行为会继续,以便在加载多个大图像后,应用程序可以爬入数百MB的“ Real Mem”内存使用量。

有趣的是,如果我一次又一次地重新加载同一张图像,则稳态内存不会增加。这是否表明正在进行某种缓存?同样,我的目标是在不增加内存的情况下通过多个不同的图像文件进行批处理。提前致谢。

哦,我正在调查“堆分析”中的ATM,但至少要发布我的进度,看看是否还有其他输入。

-

跟进#2

bbum,感谢您的精彩文章。我在测试程序中运行了仪器分配,但未发现任何堆增长。就像在引用的博客文章中一样,我的方法是:1)单击我的Interface Builder界面上的“加载和释放映像”按钮(调用加载/释放行为),2)每隔几秒钟单击“标记堆”几次在工具分配中,然后3)重复1)和2)。

使用这种方法,Heapshots随时间推移始终在Heap Growth列中报告0字节(每5秒3次单击,持续15秒),这意味着没有从基线Heapshot分配额外的内存。此外,在“统计信息”窗格中,每次单击“加载和释放映像”按钮时,都有13.25MB的Malloc,但“活动字节”为0字节,这意味着它已被完全释放。如果我单击“加载和释放映像”按钮三次,映像的“总字节数”报告为39.75MB(3 * 13.25MB),这意味着已分配39.75MB,但由于“实时字节数”为0,因此已完全释放。由于操作非常迅速,因此峰值会迅速上升并立即下降。在内存的稳态使用中没有泄漏且没有增长似乎是有道理的。

但是,现在我该怎么办,我的“真实内存”统计仍然很高?我知道活动监视器不是调试内存问题的标准。但是,“ Real Mem”仍然保持很高的状态,直到我关闭程序,然后“ Real Mem”全部回到“ Free”类别,这对我来说很奇怪。

我只用相同的方法复制了代码,就用两个图像(15MBjpeg-1.jpg,15MBjpeg-2.jpg)测试了相同的方法,但我没有再看到堆增长。显然,已经分配并释放了更多分配。但是,现在“ Real Mem”的数量大约是加载和释放一张图像时的两倍。同样,它不会在测试程序的稳态下降低。

我还能做些什么吗?这是单个图像加载/发布的测试代码,供任何尝试的人使用(只需将IB按钮连接到openFiles):

#import "TestMemory.h" // only declares -(IBAction) openFiles:(id)sender;
@implementation TestMemory
-(IBAction) openFiles:(id)sender {
NSURL *inputUrl = [NSURL URLWithString:@"file:///Users/me/Desktop/15MBjpeg.jpg"];
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
NSImage *image = [[NSImage alloc] initWithContentsOfURL:inputUrl];
[image release];
[apool drain];
}
@end


谢谢阅读。 :)

-

跟进#3

bbum,否,我没有启用垃圾收集(在项目设置中,GC设置为不支持)。我使用仪器分配中的vmmap和VM Tracker条调查了内存。当您说“ VM仪器”时,我假设您的意思是VM Tracker数据,因为它报告的信息与vmmap相同。使用“加载和释放映像”按钮打开单个映像后,一些重要的内存编号包括以下内容(来自VM Tracker):

Type         %ofRes  ResSize  VirtSize Res%  %AllDirty  DirtySize

__TEXT       38%     33.84MB  80.45MB  42%   0%         0Bytes

*Dirty* 32%28.23MB 114.99MB 24%100%17.11MB
MALLOC_LARGE 14%     13.25MB  13.25MB  100%  0%         4KB

Carbon       11%     9.86MB   9.86MB   100%  20%        3.46MB

VM_ALLOCATE  9%      8.43MB   48.17MB  18%   49%        8.43MB

...

有趣的是,单个映像的后续加载/释放将Dirty和VM_ALLOCATE类型的“ Resident Size”增加了〜.3MB,并且这些类型的“ Dirty Size”也随着时间而增加。 (VM_ALLOCATE似乎是Dirty的子集)。其他类型似乎都不会随随后的加载/发行版而改变。

我不确定究竟要从这些数据中获取什么,或者我如何使用它来使程序释放内存。似乎VM_ALLOCATE类型可能是未释放的块,但这仅是推测。基础NSImage init实现的某些部分是否可以保存图像文件的缓存而不会释放它?再次,如前所述,令我着迷的是,与第一次加载/释放相比,相同图像文件的每个后续加载/释放几乎不消耗资源(CPU,磁盘打磨等)和挂钟时间。提前致谢。






最佳答案





巴法鲁斯所说的;您是否尝试用NSAutoreleasePool包围它。
这是经典的微基准测试。尽管它肯定表明存在问题,但问题实际上可能是基准与预期的现实世界模式存在很大差异,以致于错误在于基准。
在高效设计的应用程序中,它不会多次从磁盘读取同一图像数据。
这是Heapshot analysis的主要候选者。




(感谢后续活动;有帮助!)

您描述的症状听起来像是VM泄漏(某些东西正在消耗地址而未进行分配;例如,映射的内存)或未修剪的缓存(包含VM分配)。


您是否启用了GC?如果是这样,这很容易是因为没有触发GC阈值。收集器不知道将非GC的大量分配归功于GC区域。如果您强制收集,它将在这种特殊情况下工作。
尝试查看VM工具或在命令行上使用vm_map来查看正在消耗应用程序中地址空间的内容。








关于cocoa - 加载和释放NSImage时内存继续增加,我们在Stack Overflow上找到一个类似的问题:

https://stackoverflow.com/questions/5047682/




25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com