简体   繁体   English

C ++新的内存分配碎片

[英]C++ new memory allocation fragmentation

I was trying to look at the behavior of the new allocator and why it doesn't place data contiguously. 我试图查看新分配器的行为以及为什么它不会连续放置数据。

My code: 我的代码:

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;
    }
}

So I ran this with char, int, ci. 所以我用char,int,ci运行它。 Most allocations were a fixed length from the last, sometimes there were odd jumps from one available block to another. 大多数分配是从最后一个固定长度,有时从一个可用块到另一个块有奇怪的跳跃。

sizeof(char) : 1 sizeof(char):1
Average Jump: 64 bytes 平均跳转:64字节

sizeof(int): 4 sizeof(int):4
Average Jump: 16 平均跳跃:16

sizeof(ci): 8 (int has to be placed on a 4 byte align) sizeof(ci):8(int必须放在4字节对齐上)
Average Jump: 9 平均跳跃:9

Can anyone explain why the allocator is fragmenting memory like this? 任何人都可以解释为什么分配器像这样分段内存? Also why is the jump for char so much larger then ints and a structure that contains both an int and char. 另外,为什么char的跳转比int和包含int和char的结构大得多。

There are two issues: 有两个问题:

  • most allocators store some additional data prior to the start of the block (typically block size and a couple of pointers) 大多数分配器在块开始之前存储一些额外的数据(通常是块大小和几个指针)

  • there are usually alignment requirements - modern operating systems typically allocate to at least an 8 byte boundary. 通常有对齐要求 - 现代操作系统通常分配至少8字节边界。

So you'll nearly always get some kind of gap between successive allocations. 所以你几乎总是会在连续分配之间找到某种差距。

Of course you should never rely on any specific behaviour for something like this, where the implementation is free to do as it pleases. 当然,你不应该依赖任何特定的行为来做这样的事情,实现可以自由地随意做。

您的代码包含一个错误,要知道指向它们的指针的距离(char *),否则增量的大小为(T)。

This isn't fragmentation, it's just rounding up the size of your allocation to a round block size. 这不是碎片,它只是将您的分配大小四舍五入到圆块大小。

In general programming you should not pay attention to the pattern of memory addresses returned by general purpose allocators like new . 在一般编程中,您不应该注意通用分配器(如new返回的内存地址模式。 When you do care about allocation behaviour you should always use a special purpose allocator (boost::pool, something you write yourself, etc.) 当你关心分配行为时,你应该总是使用一个特殊用途的分配器(boost :: pool,你自己编写的东西等)

The exception is if you are studying allocators, in which case you could do worse than pick up your copy of K&R for a simple allocator which might help you understand how new gets its memory. 例外情况是,如果您正在研究分配器,在这种情况下,您可能会比为简单的分配器选择K&R副本更糟糕,这可能有助于您了解new内存的获取方式。

In general, you cannot depend on particular memory placement. 通常,您不能依赖于特定的内存放置。 The memory allocator's internal bookkeeping data and alignment requirements can both affect the placement of blocks. 内存分配器的内部簿记数据和对齐要求都会影响块的放置。 There is no requirement for blocks to be allocated contiguously. 不需要连续分配块。

Further, some systems will give you even "stranger" behavior. 此外,一些系统会给你甚至“陌生”的行为。 Many modern Linux systems have heap randomization enabled, where newly-allocated virtual memory pages are placed at random addresses to make certain types of security vulnerability exploits more difficult. 许多现代Linux系统都启用了堆随机化,其中新分配的虚拟内存页面放置在随机地址中,以使某些类型的安全漏洞利用更加困难。 With virtual memory, disparate allocated block addresses do not necessarily mean that the physical memory is fragmented, as there is no requirement for the virtual address space to be dense. 对于虚拟内存,不同的分配块地址并不一定意味着物理内存是分段的,因为不需要虚拟地址空间密集。

As others have already said, basically you have no control over how the memory management system works. 正如其他人已经说过的那样,基本上你无法控制内存管理系统的工作方式。 If you allocate many singular objects, that might result in fragmentation, and there's nothing you can do against this. 如果你分配了许多单个对象,那么可能会导致碎片化,并且你无法对此做任何事情。

However, if you need your objects to be in contiguous order in memory, you can write your own memory allocator that operates on top of malloc() or new . 但是,如果您需要对象在内存中以连续顺序排列,则可以编写自己的内存分配器 ,该分配器malloc()new之上运行。 One way to control fragmentation is to allocate a larger block of memory and to then construct your actual singular objects inside this block using placement new (link to the C++ FAQ Lite) . 控制碎片的一种方法是分配更大的内存块,然后使用placement new (链接到C ++ FAQ Lite)在此块中构建实际的单个对象。

(This works of course because a call to malloc() or new T[] is guaranteed to return a contiguous block of memory. One singular allocated object cannot be fragmented; fragmentation can only results with several allocated objects.) (这当然是因为对malloc()new T[]的调用保证返回一个连续的内存块。一个单独的分配对象不能被分段;碎片只能导致多个已分配的对象。)

For small allocation, boost has a very simple allocator I've used called boost::simple_segregated_storage 对于小分配,boost有一个非常简单的分配器,我用它叫boost :: simple_segregated_storage

It creates a copy of slists of free and used blocks, all the same size. 它创建了一个自由和使用过的块的副本,大小相同。 As long as you only allocate to its set block size, you get no external fragmentation (though you can get some internal fragmentation if your block size is bigger than the requested size.) It also runs O(1) if you use it in this manner. 只要你只分配它的设置块大小,就不会产生外部碎片(如果你的块大小大于请求的大小,你可能会得到一些内部碎片。)如果你在这里使用它,它也会运行O(1)方式。 Great for small allocation the likes of which are common with template programming. 非常适合小型分配,其中模板编程很常见。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM