9

我是系统软件系的学生。现在我正在为 Windows 开发一个内存管理器。malloc()这是我对and的简单实现free()

HANDLE heap = HeapCreate(0, 0, 0);

void* hmalloc(size_t size)
{
    return HeapAlloc(heap, 0, size);
}

void hfree(void* memory)
{
    HeapFree(heap, 0, memory);
}

int main()
{
    int* ptr1 = (int*)hmalloc(100*sizeof(int));
    int* ptr2 = (int*)hmalloc(100*sizeof(int));
    int* ptr3 = (int*)hmalloc(100*sizeof(int));

    hfree(ptr2);
    hfree(ptr3);
    hfree(ptr1);

    return 0;
}

它工作正常。但我不明白是否有理由使用多个堆?好吧,我可以在堆中分配内存并获得分配内存块的地址。但在这里我使用一个堆。有理由使用多个堆吗?也许对于多线程/多进程应用程序?请解释。

4

5 回答 5

13

使用多个堆/自定义分配器的主要原因是为了更好地控制内存。通常在大量新/删除之后,内存会变得碎片化并且应用程序的性能下降(应用程序也会消耗更多内存)。在更可控的环境中使用内存可以减少堆碎片。

另一种用法是防止应用程序中的内存泄漏,您可以释放您分配的整个堆,而无需费心释放那里分配的所有对象。

另一种用法是用于紧密分配的对象,例如,如果您有一个列表,那么您可以将所有节点分配在一个较小的专用堆中,并且应用程序将获得性能,因为在迭代节点时缓存未命中率会减少。

编辑:然而,内存管理是一个很难的话题,在某些情况下它做得不对。Andrei Alexandrescu 曾发表过一次演讲,他说对于某些应用程序,将自定义分配器替换为默认分配器可以提高应用程序的性能。

于 2013-11-14T18:02:03.880 回答
5

这是一个很好的链接,详细说明了为什么您可能需要多个堆: https ://caligari.dartmouth.edu/doc/ibmcxx/en_US/doc/libref/concepts/cumemmng.htm

"Why Use Multiple Heaps?
Using a single runtime heap is fine for most programs. However, using multiple 
heaps can be more efficient and can help you improve your program's performance 
and reduce wasted memory for a number of reasons:

1- When you allocate from a single heap, you may end up with memory blocks on
   different pages of memory. For example, you might have a linked list that 
   allocates memory each time you add a node to the list. If you allocate memory for
   other data in between adding nodes, the memory blocks for the nodes could end up
   on many different pages. To access the data in the list, the system may have to 
   swap many pages, which can significantly slow your program.

   With multiple heaps, you can specify which heap you allocate from. For example, 
   you might create a heap specifically for the linked list. The list's memory blocks 
   and the data they contain would remain close together on fewer pages, reducing the 
   amount of swapping required.

 2- In multithread applications, only one thread can access the heap at a time to 
    ensure memory is safely allocated and freed. For example, say thread 1 is 
    allocating memory, and thread 2 has a call to free. Thread 2 must wait until 
    thread 1 has finished its allocation before it can access the heap. Again, this 
    can slow down performance, especially if your program does a lot of memory 
    operations.

    If you create a separate heap for each thread, you can allocate from them 
    concurrently, eliminating both the waiting period and the overhead required to 
    serialize access to the heap.


 3- With a single heap, you must explicitly free each block that you allocate. If you 
    have a linked list that allocates memory for each node, you have to traverse the 
    entire list and free each block individually, which can take some time.

    If you create a separate heap for that linked list, you can destroy it with a 
    single call and free all the memory at once.

  4- When you have only one heap, all components share it (including the IBM C and 
     C++ Compilers runtime library, vendor libraries, and your own code). If one 
     component corrupts the heap, another component might fail. You may have trouble 
     discovering the cause of the problem and where the heap was damaged.

     With multiple heaps, you can create a separate heap for each component, so if 
     one damages the heap (for example, by using a freed pointer), the others can 
     continue unaffected. You also know where to look to correct the problem."
于 2019-06-10T12:57:33.630 回答
2

一个原因是您需要在内部执行程序,例如运行模拟代码。通过创建自己的堆,您可以允许该堆具有执行权限,出于安全原因,默认情况下该权限已关闭。(视窗)

于 2013-11-14T18:10:07.133 回答
2

你有一些好的想法,这适用于 C,但在 C++ 中你有析构函数,它们运行非常重要。

您可以将所有类型视为具有构造函数/析构函数,只是逻辑上“什么都不做”。

这是关于分配器的。请参阅“伙伴算法”,它使用 2 的幂来对齐和重用东西。

如果我在某处分配 4 个字节,我的分配器可能会为 4 个字节分配分配一个 4kb 部分。这样我可以在块中放置 1024 个 4 字节的东西,如果我需要更多添加另一个块等等。

向它请求 4kb 并且它不会在 4byte 块中分配它,它可能有一个单独的用于更大的请求。

这意味着您可以将大事放在一起。如果我去 17 个字节然后 13 个字节 1 个字节和 13 个字节被释放,我只能在其中粘贴 <=13 个字节的东西。

因此,伙伴系统和 2 的幂,使用 lshift 很容易做到,如果我想要一个 2.5kb 的块,我将它分配为适合的 2 的最小幂(在这种情况下为 4kb),这样我之后就可以使用插槽<=4kb 项目。

这不是为了垃圾收集,这只是为了让事情变得更紧凑和整洁,使用你自己的分配器可以停止对操作系统的调用(取决于 new 和 delete 的默认实现,他们可能已经为你的编译器这样做了)并使 new/删除非常快。

堆压缩是非常不同的,你需要一个指向你的堆的每个指针的列表,或者某种遍历整个内存图的方法(比如吐出Java),所以当你移动东西并“压缩”它时,你可以更新所有把那个东西指向它现在的位置。

于 2013-11-14T18:16:39.227 回答
0

我唯一一次使用多个堆是在我编写了一个可以构建复杂数据结构的程序时。通过遍历数据结构并释放单个节点来释放数据结构并非易事,但幸运的是,程序只需要临时的数据结构(当它执行特定操作时),所以我使用了一个单独的堆数据结构,这样当我不再需要它时,我可以通过调用 HeapDestroy 来释放它。

于 2013-11-15T23:14:02.363 回答