在Mac OS X上,如何在64位进程中中等大小的内存分配失败?

时间:2011-03-02 14:00:34

标签: macos 64-bit memory-management nsdata

我正在构建照片书布局应用程序。应用程序经常将JPEG图像解压缩到内存位图缓冲区中。图像的大小限制在100万像素(虽然它们通常不超过1500万像素)。

有时这些缓冲区的内存分配失败:[[NSMutableData alloc] initWithLength:]返回nil。这似乎发生在系统的自由物理内存接近零的情况下。

我对virtual memory system in Mac OS X的理解是,64位进程虚拟(sic)中的分配不会失败。有16个exabyte的地址空间,我试图一次分配最多400兆字节。从理论上讲,我可以分配400亿个缓冲区而不会遇到可用地址空间的硬限制。当然,实际限制会阻止这种情况,因为交换空间受启动卷大小的限制。实际上,我只进行了很少的这些分配(少于10次)。

我不明白的是,无论物理内存有多低,分配都会失败。我认为 - 只要有交换空间,内存分配就不会失败(因为此时页面甚至都没有映射)。

该应用程序是垃圾收集。

修改

我有时间进一步深入研究这个问题,这是我的发现:

  1. 问题只发生在垃圾收集过程中。
  2. NSMutableData的分配失败时,普通malloc仍然可以成功分配相同数量的内存。
  3. 当整体物理内存接近零(交换即将发生)时,总会发生错误。
  4. 我假设NSData在垃圾回收下运行时使用NSAllocateCollectable来执行分配而不是malloc

    我的结论是,当物理内存不足时,收集器无法分配大块内存。再一次,我不明白。

6 个答案:

答案 0 :(得分:9)

答案在于libauto的实施。

从OS X 10.6开始,在64位平台上为垃圾收集内存分配了8 Gb的竞技场。对于大型分配(> = 128k)和小型(< 2048b)或中型(< 128k)分配,该竞技场被削减一半。

因此,对于10.6,你有4Gb的内存可用于垃圾收集内存的大量分配。在10.5上,竞技场的大小为32Gb,但Apple在10.6上将该大小降低到8Gb。

答案 1 :(得分:1)

另一种猜测,但可能是您的同事的计算机配置了每个用户进程设置更严格的最大内存。要检查,请键入

ulimit -a

进入控制台。对我来说,我得到:

~ iainmcgin$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 256
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 266
virtual memory          (kbytes, -v) unlimited

从我上面的设置来看,内存使用似乎没有按进程限制。出于某种原因,您的同事可能并非如此。

我正在使用Snow Leopard:

~ iainmcgin$ uname -rs
Darwin 10.6.0

答案 2 :(得分:0)

即使64位计算机理论上可以解决18 EB,但目前的处理器限制为256 TB。当然,你也没有达到这个限度。但是,进程一次可以使用的内存量仅限于可用的RAM量。操作系统还可能限制您可以使用的RAM量。根据您发布的链接,“即使对于具有4 GB或更多GB可用RAM的计算机,系统也很少将这么多RAM用于单个进程。”

答案 3 :(得分:0)

您的交换空间可能已用完。即使您有交换文件和虚拟内存,可用的交换空间量仍然受到交换文件硬盘空间的限制。

答案 4 :(得分:0)

这可能是内存碎片问题。也许在分配时没有任何单个连续400 MB的块可用?

您可以尝试在应用程序生命周期的最初阶段分配这些大块,然后堆有机会被许多较小的分配分割。

答案 5 :(得分:-1)

initWithBytes:length:尝试在活动内存中分配其整个长度,基本上相当于该大小的malloc()。如果长度超过可用内存,您将获得nil。如果您想使用NSData的大文件,我建议使用initWithContentsOfMappedFile:或类似的初始值设定项,因为它们会在需要时使用VM系统将部分文件拉入和拉出活动内存。