2010-04-27 133 views
3

我试图看看新的分配器的行为,为什么它不连续放置数据。C++新的内存分配碎片

我的代码:

struct ci { 
    char c; 
    int i; 
} 

template <typename T> 
void memTest() 
{ 
    T * pLast = new T(); 
    for(int i = 0; i < 20; ++i) { 
     T * pNew = new T(); 
     cout << (pNew - pLast) << " "; 
     pLast = pNew; 
    } 
} 

所以我用CHAR,INT,CI跑这个。大多数分配是从最后一个固定的长度,有时从一个可用的块跳到另一个块。

的sizeof(char)的:1
平均跳转:64字节

的sizeof(int)的:4
平均跳转:16

的sizeof(CI):8(INT已被放置在一个4字节对齐)
平均跳转:9

任何人都可以解释为什么分配器是像这样分割内存?此外,为什么char的跳转要大得多,然后整数和包含int和char的结构。

+0

我应该添加在调试和发布模式之间的行为是非常不同的。我认为在调试模式下的额外空间是用于调试的目的...... – 2010-04-27 20:31:53

+0

我不认为值得对通用分配器做任何假设 - 它将随着模式的不同而不同,从平台到平台。我根本不是想让你灰心丧气 - 如果你对这件事情有什么兴趣,你肯定会*继续挖掘! (相应地提出你的问题) – 2010-04-27 21:05:34

回答

8

有两个问题:

  • 最分配器存储该块的开始之前一些额外的数据(通常是块大小和几个指针)

  • 通常有对准要求,例如Linux的一般分配到至少8字节边界,OS X分配到至少16字节边界

所以你几乎总是得到某种形式的连续分配差距。

当然,你不应该依赖任何特定的行为来实现这样的事情,其实现可以随意进行。

+0

我没有打算依靠特定的行为。只是试图了解碎片来自哪里,所以我知道为什么我想要做一个巨大的块分配,并且如果我有内存限制,我自己也会自己释放内存。 – 2010-04-27 20:35:45

+0

@tamulji:是的,在某些情况下,您希望使用单个大分配并将其自行打包。出现两种情况是:(i)当你有很多非常小的分配时,以及(ii)像图像处理这样的东西,你希望你的图像是一个'T **',但是你也想要行连续。 – 2010-04-27 20:46:10

+0

同样在紧张的记忆情况下,或者如果您知道很多关于分配模式和生命周期的快速分配/解除分配 - 如果您有特定要求,您可以总是比通用分配器做得更好, gp分配器*就在那里*并且易于使用。 – 2010-04-27 21:10:00

5

您的代码包含一个错误,要知道指针的距离将它们转换为(char *),否则deltas在sizeof(T)中。

+0

是的,我发布后意识到这一点。仍然显示分裂。 – 2010-04-27 20:31:10

+1

或者转换为像ptrdiff_t或intptr_t之类的整数类型,然后才能发挥作用。 – Void 2010-04-27 20:34:16

+0

这似乎与我一致。 int和char分配相距64个字节。结构“ci”相距72个字节。你没有说你正在运行哪个操作系统,但似乎分配器正在将块放在另一个之后。 – plodoc 2010-04-27 20:49:24

1

一般来说,您不能依赖特定的内存布局。内存分配器的内部簿记数据和对齐要求都会影响块的放置。没有要求块被连续分配。另外,有些系统甚至会给你“陌生人”的行为。许多现代的Linux系统有堆随机启动,其中新分配的虚拟内存页面被放置在随机地址,使某些类型的安全漏洞攻击的难度。对于虚拟内存,不同的分配块地址不一定意味着物理内存被分割,因为虚拟地址空间不需要密集。

2

这不是碎片,它只是将您的分配大小舍入到一个圆形块大小。

在一般的编程,你不应该注重通过通用分配器像new返回的内存地址的模式。当你关心分配行为时,你应该总是使用一个特殊的分配器(boost :: pool,你自己写的东西,等等)。)

例外情况是,如果您正在学习分配器,在这种情况下,您可能会比挑选K&R的副本更简单的分配器,这可能会帮助您了解new如何获取其内存。

0

正如其他人已经说过的,基本上你无法控制内存管理系统的工作方式。 如果您分配许多单一对象,这可能导致碎片,,并且没有什么可以做到这一点。

然而,如果你需要你的对象是在连续的顺序在内存中,您可以编写自己的内存分配器上的malloc()new顶部运行。控制碎片的一种方法是分配更大的内存块,然后使用placement new(链接到C++ FAQ Lite)在此块内构建实际的单个对象。

(因为malloc()new T[]呼叫被保证返回一个连续的内存块这工作过程中的一个单数分配对象不能被分段;碎裂只能导致与几个分配的对象。)

0

对于小分配,boost有一个非常简单的分配器,我用它叫做boost :: simple_segregated_storage

它创建了所有相同大小的空闲和使用块的slists副本。只要您仅分配给其设置的块大小,就不会获得外部碎片(尽管如果块大小大于请求的大小,您可以获得一些内部碎片)。如果在此使用它,它也会运行O(1)方式。非常适合小分配,这些分配在模板编程中很常见。