如何避免Swift中的内存碎片

时间:2016-11-06 08:05:18

标签: swift garbage-collection automatic-ref-counting

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

这些陈述是否正确?

  1. 每次引用计数变为零时,分配的空间将添加到“可用”列表中。
  2. 对于下一个分配,使用可以适合该大小的最前面的内存块。
  3. 以前用过的内存的块将再次使用
  4. “可用列表”是否按地址位置或大小排序?

    活动物体是否会移动以获得更好的压实效果?

2 个答案:

答案 0 :(得分:5)

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

malloc()在C库(libc)中实现。您可以看到Apple的libc here实施。 malloc()中定义了malloc。如果您对使用的内存分配算法感兴趣,可以继续沿着兔子洞进行旅程 那里。

  

“可用列表”是否按地址位置或大小排序?

这是libc的实施细节,欢迎您在上面链接的源代码中发现。

  

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

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

  

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

不一定是最重要的。有许多不同的内存分配方案。你想到的那个被称为“第一次适合”。以下是一些示例内存分配技术(来自/gen/malloc.c):

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

  •   
  • 首先适合:内存中可能有很多漏洞,因此操作系统会减少分析可用空间所花费的时间,从主内存开始处开始并从遇到足够大的第一个洞中分配内存以满足请求。使用与上面相同的示例,first fit将为流程分配12KB的14KB块。

  •   
  • 最差:内存管理器将进程放在可用的最大未分配内存块中。我们的想法是,此分配将在分配后创建最大的保留,从而增加了与最佳匹配相比,另一个过程可以使用剩余空间的可能性。使用与上面相同的示例,最差拟合将为流程分配12KB的19KB块,留下7KB块供将来使用。
  •   

对象在其生命周期内不会被压缩。 {{1}}通过分配内存的方式处理内存碎片。它无法移动已经分配的对象。

答案 1 :(得分:0)

亚历山大的答案很棒,但还有一些与内存布局有关的其他细节。垃圾收集需要内存开销来进行压缩,因此malloc碎片浪费的空间并没有真正使它处于劣势。移动内存也会影响电池和性能,因为它会使处理器缓存无效。 Apple的内存管理实现可以压缩一段时间内未访问过的内存。即使虚拟地址空间可以分段,实际的RAM也会因压缩而碎片化程度较低。压缩还允许更快地切换到磁盘。

相关性较低,但Apple挑选引用计数的一个重要原因与c调用内存布局有关。如果您与c-library进行大量交互,Apple的解决方案会更好。垃圾收集系统通常与c交互的速度要慢一些,因为它需要在调用之前停止垃圾收集操作。开销通常与任何语言的系统调用大致相同。通常,除非您在循环中调用c函数(例如使用OpenGL或SQLite),否则无关紧要。其他线程/进程通常可以使用处理器资源,而c调用正在等待垃圾收集器,因此如果您可以在几次调用中完成工作,则影响很小。将来,当涉及到系统编程和类似生锈的生命周期内存管理时,Swift的内存管理可能会有所优势。它是在Swift的路线图上,但Swift 4还不适合系统编程。通常在C#中,您将使用托管c ++进行系统编程和大量使用c库的操作。