2016-11-06 58 views
4

GC的压缩,扫描和标记避免了堆内存碎片。那么在Swift中如何避免内存碎片?Swift中如何避免内存碎片

这些陈述是否正确?

  1. 每当引用计数变为零时,分配的空间就会被添加到“可用”列表中。
  2. 对于下一个分配,可以使用适合大小的最前面的内存块。先前使用的内存
  3. 大块将再次曾经是最好尽可能

是通过地址位置或大小排序的“可用列表”?

为了更好的压实,活物是否会移动?

回答

5

我做了一些编译Swift程序的汇编,我发现swift:: swift_allocObject是一个新的Class对象实例化时调用的运行时函数。它调用SWIFT_RT_ENTRY_IMPL(swift_allocObject),它调用swift::swift_slowAlloc,最终调用C标准库中的... malloc()。所以Swift的运行时没有执行内存分配,它是malloc()

malloc()在C库(libc)中实现。你可以看到苹果的执行libcheremalloc()定义于/gen/malloc.c。如果您对使用哪种内存分配算法感兴趣,则可以继续从那里的 下拉兔子洞。

'可用列表'是按地址位置还是大小排序?

这是一个malloc的实现细节,我欢迎您在上面链接的源代码中发现。

1.每当引用计数变为零时,分配的空间就会被添加到“可用”列表中。

是的,这是正确的。除了“可用”列表可能不是一个列表。此外,此操作不一定由Swift运行时库完成,但可以通过系统调用由OS内核完成。

2.对于下一个分配,可以使用适合大小的最前面的内存块。

不一定是最前面的。有许多不同的内存分配方案。你想到的那个叫做“第一个适合”。以下是一些示例性的存储器分配技术(来自this site):

  • 最佳拟合:分配器放置一个过程中未分配存储器将在其中适合的最小的块。例如,假设一个进程请求12KB内存,并且内存管理器当前有一个6KB,14KB,19KB,11KB和13KB块的未分配块列表。最合适的策略是将12KB的13KB块分配给进程。

  • 首先拟合:有可能是在存储器的许多孔,因此操作系统,以减少它花费分析可用空间的时间量,从第一开始于主存储器的起始和分配内存它遇到足够大的孔以满足要求。使用与上面相同的示例,首次拟合将为进程分配12KB的14KB块。

  • 最坏情况:内存管理器将进程放入可用的最大未分配内存块中。我们的想法是,这种布局会在分配之后创建最大的保留位置,从而增加与最佳匹配相比,另一个进程可以使用剩余空间的可能性。使用与上述相同的示例,最差配合将为该进程分配12KB的19KB块,留下7KB块供将来使用。

对象在使用期限内不会被压缩。 libc通过分配内存的方式处理内存碎片。它无法移动已分配的对象。

0

亚历山大的回答是伟大的,但也有一定的相关性内存布局一些其他细节。垃圾收集需要用于压缩的内存开销,所以从malloc碎片中浪费的空间实际上并没有使它处于劣势。移动内存也会对电池和性能产生影响,因为它会使处理器缓存无效。 Apple的内存管理实现可以压缩一段时间内尚未访问的内存。尽管虚拟地址空间可以被分割,但实际的RAM由于压缩而不易碎化。压缩还允许更快地交换到磁盘。

较少有关,但大的原因,苹果采摘引用计数更多的是与c-调用,那么内存布局之一。如果您与c-libraries大量交互,Apple的解决方案效果会更好。垃圾收集系统通常与c进行交互的速度级别较低,因为它需要在调用之前暂停垃圾收集操作。开销通常与任何语言的系统调用大致相同。通常情况下,除非您在循环中调用c函数,例如使用OpenGL或SQLite,这并不重要。其他线程/进程通常可以使用处理器资源,而c调用正在等待垃圾收集器,所以如果您可以在几个调用中完成工作,影响最小。在未来,当涉及到系统编程和类似生锈的生命周期内存管理时,Swift的内存管理可能会有优势。它在Swift的路线图上,但Swift 4还不适合系统编程。通常在C#中,您可以使用托管C++来进行大量使用c库的系统编程和操作。