感觉很奇怪,但我会尝试自己回答。我可以得到一些评论:
如果以繁重的方式(循环)分配和释放非常大的对象,两个平台都会出现碎片。对于非托管应用程序,直接从虚拟地址空间进行分配。通常工作数组将被包装在一个类 (c++) 中,为简洁的语法提供运算符重载、一些引用处理和一个析构函数,确保在超出范围时立即释放数组。然而,请求的数组并非始终具有相同的大小 - 如果请求更大的数组,则无法重用相同的地址块,这可能会随着时间的推移导致碎片。此外,没有办法找到块,它正好满足请求的数组长度。操作系统将简单地使用足够大的第一个块 - 即使它根据需要更大,并且可能更好地满足对稍后即将推出的更大阵列的请求。汇集如何改善这种情况?
可以想象的是,使用更大的数组来处理更小的请求。该类将处理从底层数组的真实长度到外部世界所需的虚拟长度的转换。池可以帮助提供“第一个足够长的数组” - 与操作系统相反,它总是会给出确切的长度。这可能会限制碎片,因为在虚拟地址空间中创建的漏洞更少。另一方面,整体内存大小会增加。对于几乎随机的分配模式,池化几乎不会带来任何利润,但我猜只会吃掉稀有的内存。
在管理方面,情况更糟。首先,存在两个可能的碎片目标:虚拟地址空间和托管大对象堆。在这种情况下,后者将更多是从操作系统单独分配的单个序列的集合。每个 seqment 主要只用于单个数组(因为我们在这里谈论的是非常大的数组)。如果 GC 释放了一个数组,则将整个段返回给操作系统。因此,在 LOH 中,碎片化不会成为问题(参考:我自己的想法和使用 VMMap 的一些经验观察,因此非常欢迎任何评论!)。
但是由于 LOH 段是从虚拟地址空间分配的,因此这里的碎片也是一个问题——就像非托管应用程序一样。事实上,这两个应用程序的分配模式对于操作系统的内存管理器来说应该非常相似。(?) 有一个区别:GC 同时释放了所有数组。但是,“非常大的数组”会对GC产生很大的压力。在收集发生之前,只能同时保存相对较少数量的“非常大的数组”。基本上,应用程序通常会在 GC 中花费合理的时间(大约 5..45%),这也是因为几乎所有的回收都是昂贵的 Gen2 回收,并且几乎每次分配都会导致这样的 Gen 2 回收。
在这里汇集可能会有很大帮助。一旦阵列没有被释放到操作系统,而是被收集到池中,它们就可以立即用于进一步的请求。(这是 IDisposable 不仅适用于非托管资源的原因之一)。框架/库只需要确保足够早地将数组放入池中,并允许在实际需要较小尺寸的情况下重用较大的数组。