Rus*_*one 21 memory-management rust
Rust编程语言的自动内存管理是否需要回收碎片内存?如果是这样,它是如何做到的?
我的理解是,它的类型系统(所有制类型,借款Rc,Arc)允许其确定性在编译时知道什么时候分配的内存块可以被释放.
但是,内存块是否可能在一个订单中分配,并以不同的顺序释放,从而导致碎片?如果这被阻止了,怎么样?如果确实发生了这种情况,那么如何有效地管理内存片段?如果他们进行碎片整理,那么使用的方法是什么?
Mat*_* M. 42
TL; DR:大多数程序永远不必担心C,C++或Rust中的碎片.那些人必须自己处理它.
Rust编程语言的自动内存管理是否需要回收碎片内存?
Rust没有自动内存管理功能; 它具有手动内存管理,编译器会检查其是否正确.差异可能听起来很理论,但这很重要,因为它意味着内存操作直接映射到源代码,幕后没有任何魔力.
通常,语言需要具有压缩GC才能压缩碎片存储器.Rust,就像C和C++一样,没有GC,所以它的内存可能会根据使用情况而碎片化,如果没有程序释放恼人的块,就无法进行碎片整理,因为重定位是不可能的.
但是,在我们开始担心碎片化之前,我们必须先考虑它的含义.
碎片有什么影响?
碎片会造成物理内存和地址空间的浪费:您的程序占用的空间超过了它的使用量.在极端情况下,即使未使用的内存量足以授予它们,这种浪费也可能阻止分配请求.
当与GC语言并行时,重要的是要意识到大多数GC语言也会造成一些浪费.
实际上,值得注意的是,支离破碎不是唯一的浪费来源; 过度分配也是一个常见的"问题":
Vec将分配2个幂的元素,但也许你只使用2^N + 1,浪费2^N - 1插槽BTreeMap或HashMap分配比实际使用的空间更多的空间这甚至不计算管理这个内存所带来的浪费,因为内存分配器维护一些状态来知道它有哪些页面,以及它们中使用了什么.
这强调了一个非常重要的事实:如果因为你的分配方案规定你会遇到同样的问题,你会消耗这么多内存,那么摆脱碎片是没有意义的.
现代分配器如何管理内存?
现代分配器不是你的da的自由列表分配器.
典型的分配方案相对简单,但非常擅长保持小型请求的碎片化:
对于小板块,许多类由大小定义.例如:(0, 8],(8, 12],(12, 16],... (164, 196],... (..., 512].每个类大小管理自己的OS页面列表,并将每个OS页面刻为自己的私有用途.4kB OS页面上512字节类的示例可以是:
+---+---+---+---+---+---+---+---+
| a | b | c | d | e | f | g | h |
+---+---+---+---+---+---+---+---+
Run Code Online (Sandbox Code Playgroud)
其中,512字节插槽a通过g可用于分配和最新的插槽h被保留用于元数据(免费插槽,在同一类下一个/上页等).请注意,类大小越大,在最后一个插槽中浪费的越多,这就是更大的分配使用不同方案的原因.
取消分配时,页面保持在类大小,直到最后一个插槽被释放,此时页面再次为空,可以用于另一个组.
注意:默认情况下,Rust可执行文件使用jemalloc,本文有关于它的具体细节.
对于内存消耗意味着什么?
小平板方案1的最大内存消耗是多个OS页面,可以计算为每个桶大小消耗的OS页面的最大数量之和,其本身是此大小中的最大并发分配数乘以划分通过适合页面的分配数量(并向上舍入).
这是因为如果您分配给定大小的1000个插槽,以随意的方式释放大部分插槽,在OS页面中挖出漏洞,然后重新分配相同大小的插槽,直到再次达到1000 ...然后您的记忆消耗是不变的,因为分配器将使用已经部分填充的OS页面中的空闲槽来完成第二波分配.
这意味着小班级的分配既快又好,但对分片没有多大贡献.
当然,这忽略了一个程序的情况,该程序将进行1M 1字节分配,以一种使所有页面都使用的方式释放大部分,然后用2字节,3字节等进行相同的操作......但这看起来像一个病态的案例.
1 是的,我正在撒谎.您还需要考虑分配器的内部结构开销以及它可以缓存一些未使用的操作系统页面以备将来分配的事实,......但是,它足以解释碎片的影响.
那么,碎片是一个问题吗?
好吧,它仍然可以.尽管在OS页面的粒度上,地址空间仍然可以分段.
对于虚拟内存,RAM不需要是连续的,因此只要有足够的空间就可以使用页面.也就是说,即使存储器物理地遍布整个RAM,地址空间也给用户提供了连续存储器的错觉.
存在的问题是:这种连续记忆的错觉需要找到一个地址空间的连续区域,这个区域会受到碎片化.
这种碎片不会出现在小请求中,但对于超过页面大小的请求,它们可能是个问题.目前,使用64位指针,这在实践中的问题要小得多(即使仅使用47位用户用户),但在32位程序中,它更容易浮出水面:例如,它是mmap32位地址空间中的2GB文件非常困难,因为它立即占据了它的一半...假设没有杂散分配阻止它(在这种情况下请求将失败).
战斗输了吗?
那么,系统编程的主要优点是......你可以说系统语言.
如果您的程序具有典型分配器无法很好处理的分配行为,您可以:
sbrk/您mmap自己处理它们)10年来,我个人从未需要,只是在业余时间写作分配器以获得乐趣; 但这是可能的.
总结Matthieu的详尽详尽的解释-
Rust,C和C ++在使用其标准内存管理时,确实会导致内存碎片化。它们不进行碎片整理。
但是,在大多数现实世界的用例中,碎片非常少,所以这不是问题。
如果有问题,可以滚动自己的分配器。