内存映射文件和低内存场景

39
iOS平台在低内存情况下如何处理内存映射文件?所谓的低内存情况是指当操作系统向应用程序中所有观察者发送UIApplicationDidReceiveMemoryWarningNotification通知时。
我们使用+[NSData dataWithContentsOfMappedFile:]将文件映射到内存中,其文档说明如下:

映射文件使用虚拟内存技术,避免将文件页面复制到内存中,直到实际需要它们为止。

这是否意味着操作系统会在不使用这些页面时取消映射?是否可以标记这些页面已不再使用?如果这些数据是只读的,那么情况会有所改变吗?如果直接使用mmap(),是否更可取?

1
由于这是一个非常有趣的问题,我想看到答案,因此我开了一个悬赏。 - DarkDust
6个回答

20

内存映射文件将数据一次一页地从磁盘复制到内存中。除非使用 mlock(2) 将页面固定在物理内存中,否则未使用的页面可以自由交换,就像其他虚拟内存一样。内存映射留下了从磁盘复制到内存以及何时进行操作的决定权给操作系统。

从基础级别降至BSD级别以使用 mmap 不太可能产生太大的差异,除了使必须与其他基础代码接口的代码更为笨拙之外。


感谢您的回答,Jeremy。我明白使用mmap文件可以让操作系统拥有更大的控制权,但我的问题与iOS如何处理这个问题有关。我的唯一选择是像您在评论中建议的那样进行实验性测试并使用mincore(非常有用,谢谢!)吗? - Aidan Steele
不幸的是,iOS的源代码并不公开。Darwin是Mac OS X的基础,与iOS非常相似,并且具有公共源代码,因此您可以查看该代码;您可能只需要查看“xnu”软件包。除此之外,您唯一的选择是实验和逆向工程,前者可能会更快,尽管没有那么有趣。 - Jeremy W. Sherman
此外,尽管您可能能够确定iOS现在的行为,但除非有某种承诺的行为方式(标准合规性,苹果文档),否则未来的iOS版本将可以自由地表现出不同的行为。 - Jeremy W. Sherman
由于问题并没有完全得到解答(我们仍然没有确认它是否像您怀疑的那样运作),我将让悬赏过期,这将授予您25个积分。此外,我对该问题进行了+1操作,这将为您带来35个积分,希望您能接受。 - DarkDust
@DarkDust 没问题。此时,我希望 OP 进行所需的分析以确定 iOS 的行为,然后编辑问题或添加评论并附上结果。 - Jeremy W. Sherman

11

(这不是一个答案,但这将是有用的信息。)

@ID_AA_Carmack的推文中得知:

@ID_AA_Carmack是否在低内存条件下自动取消映射iOS内存映射文件? (使用+ [NSData dataWithContentsOfMappedFile]?)

ID_AA_Carmack回答道,

@KhrobEdmonds是的,这就是在iOS上使用映射文件的巨大好处之一。我使用mmap(),但是。

我不确定这个回答是否正确...


6
如果John Carmack确认了它,对我来说几乎等同于苹果的确认;-) 他会测试这些东西并且知道他在谈论什么。 - DarkDust

7
根据我的实验,NSData不会响应内存警告。我通过创建一个内存映射的NSData并访问文件的部分内容来进行测试,以便将其加载到内存中,并最终发送内存警告。在内存警告后,内存使用量没有减少。文档中没有说明内存警告会导致NSData在低内存情况下减少实际内存使用量,因此我认为它不会响应内存警告。例如,NSCache文档说它会尝试与内存使用有关的好方法,而且我被告知它会响应系统引发的低内存警告。
另外,在iPod Touch(第4代)上进行简单测试时,我能够将约600兆字节的文件数据映射到虚拟内存中使用+[NSData dataWithContentsOfMappedFile:]。接下来,我开始通过NSData实例上的bytes属性访问页面。当我这样做时,实际内存开始增长,但在大约30兆字节的实际内存使用量处停止增长。因此,从实现方式来看,它似乎限制了实际内存使用量的上限。
简言之,如果要减少NSData对象的内存使用量,最好确保它们完全被释放,并且不依赖于系统自动为您执行的任何操作。

1
是的,虚拟内存将被分页到 RAM 中,直到达到计算出的 RAM 限制为止。位于虚拟内存中的进一步数据请求将迫使内核将不活动的数据分页出去,从标记适当的块开始。明知道使用虚拟内存映射数据并不真正依赖于内核的魔力:D - some_id

4
如果iOS像其他Unix一样 - 我敢打赌在这方面是这样的 - 在mmap()区域中的页面不会被“交换出去”;它们只是被丢弃(如果它们是干净的)或被写入基础文件,然后被丢弃(如果它们是脏的)。这个过程被称为“驱逐”页面。
由于您的内存映射是只读的,因此页面始终是干净的。
当物理内存紧张时,内核将决定要驱逐哪些页面。
您可以使用posix_madvise()向内核提供有关希望保留/驱逐哪些页面的提示。特别是,POSIX_MADV_DONTNEED告诉内核可以随意驱逐这些页面;或者正如您所说,“标记页面不再使用”。
编写一些测试程序以查看iOS是否支持“不需要”提示应该非常简单。由于它来自BSD,我敢打赌它会支持。

1

针对文件支持的内存的标准虚拟内存技术表明,操作系统可以随时重新获取页面,因此可以自由地丢弃页面。我没有使用过iOS,但这已经是许多其他操作系统长期以来的虚拟内存行为。

测试它的最简单方法是将几个大文件映射到内存中,通过阅读它们来保证将它们分页到内存中,并查看是否可以强制出现低内存情况。如果不能,则操作系统必须在决定不再使用它们后取消映射页面。


3
你可能会发现 mincore 函数很有用,它可以测试页面是否被换出。 - Jeremy W. Sherman
我可能需要走这条路,看看我能通过实验确定什么。我问了我的问题,因为似乎iOS并没有严格遵守“标准虚拟内存技术”,并且不会将未使用的内存交换到磁盘上等等,我想知道这如何转化为mmap映射的文件。 - Aidan Steele

0

dataWithContentsOfMappedFile: 方法从 iOS5 开始已经被弃用。

使用 mmap,可以避免这些情况。


4
我认为这是因为+[NSData dataWithContentsOfFile:options:error:]已经替代了它,它支持映射文件。 - Aidan Steele
那个功能从iOS 2.0就已经可用了。顺便说一下,如果你对这个话题感兴趣,这本书有一个很好的章节,以及其他内核内容。http://www.apress.com/9781430235361-4892 :D - some_id

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接