内存分配器

3
我希望在Windows上使用C++创建一个虚拟分配器,将数据分配到硬盘上的文件中,以减少分配大型对象时物理内存的使用!我不想使用VirtualAlloc等系统虚拟内存。我想创建一个文件并使用它来分配整个对象,然后将所需部分或对象移动到RAM中。
我尝试使用内存映射文件,但遇到了一些问题:我使用映射文件来分配向量元素,但当我删除其中任何一个元素时,元素的地址会改变。此外,我找不到一种方法只在需要时映射对象(在我的测试中,我映射了整个文件)!
有没有任何资源或开源项目可以提供帮助?

6
一般来说,操作系统比你更擅长决定哪些对象需要保留在内存中,哪些需要移出到磁盘上。虽然作为程序员,你对内存使用方式了解更多,但操作系统具有更全局的可用内存信息。如果你想提高性能,我建议不要这样做,除非你真的知道自己在做什么。 - Michiel Buddingh
只是为了支持Michiel所说的 - 你所描述的就是虚拟内存所做的事情 - 让操作系统来处理它。 - anon
是的,我描述了虚拟内存的功能,因为我想要创建一个"自定义内存分配器",但又不占用过多的内存空间或页面文件。 - Ahmad
1
一般来说,如果你要处理大量的数据,应该从主内存或页面文件中取出大块空间--这就是它们的作用。将它们闲置是浪费资源。 - Michiel Buddingh
5个回答

3

谷歌可以帮助您。我几年前实现了一个使用共享内存存储的自定义STL分配器。相同的技术可用于实现磁盘支持的分配器。我建议您先查看这个SourceForge项目,以获取灵感。


2

谢谢,我了解Boost,但还没有尝试过。我刚刚尝试使用映射文件工作。 - Ahmad

1

抱歉,但您似乎不理解(虚拟)内存的工作原理。一方面,您说“我想制作一个'自定义内存分配器',但不占用大量内存空间”,但另一方面,您却感到“元素的地址已更改”而感到惊讶。

这是可以预料的。为了确保(逻辑)对象的地址不会更改,您必须将该地址表示的内存保留给该对象。如果您释放了内存,则该内存可供重复使用,因此地址也可以重复使用。如果地址被重复使用,则无法将对象页回到该地址。

最终,问题在于地址和内存非常紧密地相互关联。回收内存意味着回收地址。


我对地址的改变并不感到惊讶,但我希望有解决方案,因为当我分配一个对象时,我返回了地址值,所以程序应该在其余代码中使用它! 因此,我希望有一种适当的方法来重新访问我分配的对象! - Ahmad
你还没有理解。当你返回一个地址时,这是对象内存的地址。你不能将此内存用于其他用途,因为你告诉程序的其余部分,该地址处的内存是存储新对象的位置。 - MSalters
我的意思是,当我第一次映射文件并分配对象时,它会给我一个地址,然后取消映射此部分并映射另一个部分,那么在第二次中,第一部分映射到另一个地址,因此我只需映射整个文件一次即可,但我不需要这样做! - Ahmad

0

来自http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=213

POSIX头文件包括内存映射系统调用和数据结构。由于这个接口比Windows更直观和简单,我将我的内存映射示例基于POSIX库。

mmap()系统调用:

caddr_t mmap(caddress_t map_addr, 
       size_t length, 
       int protection, 
       int flags, 
       int fd, 
       off_t offset);

让我们来看看每个参数的含义。

在下面的示例中,程序将命令行传递的文件的前4 KB映射到其内存中,然后从中读取int值:

#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>

 int main(int argc, char *argv[])
 {
 int fd;
 void * pregion;
 if (fd= open(argv[1], O_RDONLY) <0)
 {
 perror("failed on open");
 return1;
 }
 /*map first 4 kilobytes of fd*/
 pregion=mmap(NULL, 4096, PROT_READ,MAP_SHARED,fd,0);
 if (pregion==(caddr_t)-1)
 {
 perror("mmap failed")
 return1;
 }
 close(fd); //close the physical file because we don't need it
 //access mapped memory; read the first int in the mapped file
 int val= *((int*) pregion);
}

使用munmap()函数取消映射一个已经映射的区域:
int munmap(caddr_t addr, int length);

addr是正在取消映射的区域的地址。length指定应取消映射多少内存(您可以取消映射先前映射的部分区域)。以下示例取消映射了先前映射文件的第一个千字节。在此调用之后,剩余的三个KB仍然映射到进程的RAM中:

munmap(pregion, 1024);

我使用了CreateFileMapping和MapViewOfFile来映射文件的一部分,但是我无法访问未映射的对象,并且在分配对象时返回地址,因此当映射未映射的对象时,地址会发生变化! 因此,我想要一种在需要时映射对象的方法! 或者甚至另一种在文件中分配对象的方式!谢谢; - Ahmad

0

解决这个问题的最佳方法可能不是返回大对象的常规指针,而是返回小代理。这些代理对象实现了较大对象的完整接口。但是,这些代理对象可以处理原始数据存储在RAM或磁盘上的情况。代理之间实现了LRU机制以优化RAM使用。调用者从未看到这些代理的地址发生变化,也不会得到任何原始数据的指针。


仅供扩展,代理可以使用内存映射块的偏移量而不是指针。您需要起始内存的地址(可能会更改),然后通过偏移量可以访问任何数据。我不会尝试为此制作分配器,但是会制作一个支持STL迭代器的新容器。然后您就可以访问现有的STL算法库、容器适配器等。 - KeithB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接