有关内存映射的效率讨论,我想节省内存使用.

micr0soft 2010-05-19 12:10:42
我的软件需要大概有200M的数据需要处理,我之前的做法是把数据都装到内存,但是这样软件就显得占用内存过大,我上网查资料发现很多人使用内存映射技术,但是我发现一个问题,那就是内存映射是不是也是把文件的数据调入到内存里去执行,那么这样的话与直接调内存有什么大区别,还有,如果采取不断映射一小段数据到内存,虽然这样可以节省内存,但是频繁的改变映射的地址区域会不会造成效率的下降?那样就得不偿失了.
...全文
469 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
liuharris 2010-05-19
  • 打赏
  • 举报
回复
使用内存映射文件时就不必再对文件执行I/O操作了,对文件进行处理也不必再为文件申请并分配缓存,所有的文件缓存操作都是系统直接管理,也就是取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,所以在处理大数据量的文件时效率比较高
副组长 2010-05-19
  • 打赏
  • 举报
回复
200M根本就不用什么内存映射,直接申请内存就可以。实验结果表明XP一次申请500M以下、Win7 1G以下都是比较可靠的,前提是系统安装了足够的内存。
如果你需要腾出内存空间有别的用途,内存映射是一个不错的方案。需要时把内存映射文件映射到内存,像操作内存一样的效率,不需要时把它交换出去(需要点时间),腾出内存做别的用途。
200M左右就不要改变映射地址了,要是很大,比方几个G还有点意义。
jingzhongrong 2010-05-19
  • 打赏
  • 举报
回复
数据肯定是要在内存中的,200M的数据其实并不算多,如果你要减少内存使用,那只能分段读取,另外关于文件操作和内存映射,效率应该差不多,你可以测试测试。
向立天 2010-05-19
  • 打赏
  • 举报
回复
你的担心是多余的
放心的使用内存映射就好了
wangli820 2010-05-19
  • 打赏
  • 举报
回复
用内存映射文件处理大文件应用示例
  下面结合一个具体的实例来进一步讲述内存映射文件的使用方法。该实例从端口接收数据,并实时将其存放于磁盘,由于数据量大(几十GB),在此选用内存映射文件进行处理。下面给出的是位于工作线程MainProc中的部分主要代码,该线程自程序运行时启动,当端口有数据到达时将会发出事件hEvent[0],WaitForMultipleObjects()函数等待到该事件发生后将接收到的数据保存到磁盘,如果终止接收将发出事件hEvent[1],事件处理过程将负责完成资源的释放和文件的关闭等工作。下面给出此线程处理函数的具体实现过程:   ……// 创建文件内核对象,其句柄保存于hFileHANDLE hFile = CreateFile("Recv1.zip",GENERIC_WRITE | GENERIC_READ,FILE_SHARE_READ, NULL,CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);// 创建文件映射内核对象,句柄保存于hFileMappingHANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READWRITE, 0, 0x4000000, NULL);// 释放文件内核对象CloseHandle(hFile);// 设定大小、偏移量等参数__int64 qwFileSize = 0x4000000;__int64 qwFileOffset = 0;__int64 T = 600 * sinf.dwAllocationGranularity;DWORD dwBytesInBlock = 1000 * sinf.dwAllocationGranularity;// 将文件数据映射到进程的地址空间PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock);while(bLoop) {// 捕获事件hEvent[0]和事件hEvent[1]DWORD ret = WaitForMultipleObjects(2, hEvent, FALSE, INFINITE); ret -= WAIT_OBJECT_0;switch (ret){// 接收数据事件触发case 0:// 从端口接收数据并保存到内存映射文件nReadLen=syio_Read(port[1], pbFile + qwFileOffset, QueueLen);qwFileOffset += nReadLen;// 当数据写满60%时,为防数据溢出,需要在其后开辟一新的映射视图if (qwFileOffset > T){T = qwFileOffset + 600 * sinf.dwAllocationGranularity;UnmapViewOfFile(pbFile);pbFile = (PBYTE)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock);}break;// 终止事件触发case 1:bLoop = FALSE;// 从进程的地址空间撤消文件数据映像UnmapViewOfFile(pbFile);// 关闭文件映射对象CloseHandle(hFileMapping);break;}}…   在终止事件触发处理过程中如果只简单的执行UnmapViewOfFile()和CloseHandle()函数将无法正确标识文件的实际大小,即如果开辟的内存映射文件为30GB,而接收的数据只有14GB,那么上述程序执行完后,保存的文件长度仍是30GB。也就是说,在处理完成后还要再次通过内存映射文件的形式将文件恢复到实际大小,下面是实现此要求的主要代码:   // 创建另外一个文件内核对象hFile2 = CreateFile("Recv.zip", GENERIC_WRITE | GENERIC_READ,FILE_SHARE_READ, NULL,CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN,NULL);// 以实际数据长度创建另外一个文件映射内核对象hFileMapping2 = CreateFileMapping(hFile2,NULL, PAGE_READWRITE,0,(DWORD)(qwFileOffset&0xFFFFFFFF),NULL);// 关闭文件内核对象CloseHandle(hFile2);// 将文件数据映射到进程的地址空间pbFile2 = (PBYTE)MapViewOfFile(hFileMapping2, FILE_MAP_ALL_ACCESS, 0, 0, qwFileOffset);// 将数据从原来的内存映射文件复制到此内存映射文件memcpy(pbFile2, pbFile, qwFileOffset);file://从进程的地址空间撤消文件数据映像UnmapViewOfFile(pbFile);UnmapViewOfFile(pbFile2);// 关闭文件映射对象CloseHandle(hFileMapping);CloseHandle(hFileMapping2);// 删除临时文件DeleteFile("Recv1.zip");
wangli820 2010-05-19
  • 打赏
  • 举报
回复
内存映射文件相关函数
  在使用内存映射文件时,所使用的API函数主要就是前面提到过的那几个函数,下面分别对其进行介绍:   HANDLE CreateFile(LPCTSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);   函数CreateFile()即使是在普通的文件操作时也经常用来创建、打开文件,在处理内存映射文件时,该函数来创建/打开一个文件内核对象,并将其句柄返回,在调用该函数时需要根据是否需要数据读写和文件的共享方式来设置参数dwDesiredAccess和dwShareMode,错误的参数设置将会导致相应操作时的失败。   HANDLE CreateFileMapping(HANDLE hFile,LPSECURITY_ATTRIBUTES lpFileMappingAttributes,DWORD flProtect,DWORD dwMaximumSizeHigh,DWORD dwMaximumSizeLow,LPCTSTR lpName);   CreateFileMapping()函数创建一个文件映射内核对象,通过参数hFile指定待映射到进程地址空间的文件句柄(该句柄由CreateFile()函数的返回值获取)。由于内存映射文件的物理存储器实际是存储于磁盘上的一个文件,而不是从系统的页文件中分配的内存,所以系统不会主动为其保留地址空间区域,也不会自动将文件的存储空间映射到该区域,为了让系统能够确定对页面采取何种保护属性,需要通过参数flProtect来设定,保护属性PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY分别表示文件映射对象被映射后,可以读取、读写文件数据。在使用PAGE_READONLY时,必须确保CreateFile()采用的是GENERIC_READ参数;PAGE_READWRITE则要求CreateFile()采用的是GENERIC_READ|GENERIC_WRITE参数;至于属性PAGE_WRITECOPY则只需要确保CreateFile()采用了GENERIC_READ和GENERIC_WRITE其中之一即可。DWORD型的参数dwMaximumSizeHigh和dwMaximumSizeLow也是相当重要的,指定了文件的最大字节数,由于这两个参数共64位,因此所支持的最大文件长度为16EB,几乎可以满足任何大数据量文件处理场合的要求。   LPVOID MapViewOfFile(HANDLE hFileMappingObject,DWORD dwDesiredAccess,DWORD dwFileOffsetHigh,DWORD dwFileOffsetLow,DWORD dwNumberOfBytesToMap);   MapViewOfFile()函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject为CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的保护属性实行有效控制。MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:   SYSTEM_INFO sinf;GetSystemInfo(&sinf);DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;   参数dwNumberOfBytesToMap指定了数据文件的映射长度,这里需要特别指出的是,对于Windows 9x操作系统,如果MapViewOfFile()无法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL);但是在Windows 2000下,MapViewOfFile()只需要为必要的视图找到足够大的一个区域即可,而无须考虑整个文件映射对象的大小。   在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下:   BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);   唯一的参数lpBaseAddress指定了返回区域的基地址,必须将其设定为MapViewOfFile()的返回值。在使用了函数MapViewOfFile()之后,必须要有对应的UnmapViewOfFile()调用,否则在进程终止之前,保留的区域将无法释放。除此之外,前面还曾由CreateFile()和CreateFileMapping()函数创建过文件内核对象和文件映射内核对象,在进程终止之前有必要通过CloseHandle()将其释放,否则将会出现资源泄漏的问题。   除了前面这些必须的API函数之外,在使用内存映射文件时还要根据情况来选用其他一些辅助函数。例如,在使用内存映射文件时,为了提高速度,系统将文件的数据页面进行高速缓存,而且在处理文件映射视图时不立即更新文件的磁盘映像。为解决这个问题可以考虑使用FlushViewOfFile()函数,该函数强制系统将修改过的数据部分或全部重新写入磁盘映像,从而可以确保所有的数据更新能及时保存到磁盘。
wangli820 2010-05-19
  • 打赏
  • 举报
回复
内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。   内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。下面给出使用内存映射文件的一般方法:   首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。
finder_zhang 2010-05-19
  • 打赏
  • 举报
回复
假设一下,楼主现在的电脑有512内存,要运行需要200M的一个软件,然后其他的软件及系统要用500M或更大,只是假设.那么总量加起来,超过了512的内存.

我之前测试过一个代码,如果在设置里面,设了不使用硬盘的虚拟内存,那么我先运行了其他的程序后,再运行我的程序,会出现无法分配内存的情况.但设置了硬盘虚拟内存,我的软件可以正常运行了.
总数超过了512,但可运行,WINDOWS核心编程有介绍,说是WINDOWS在读内存时,如果发现要读的数据在硬盘的虚拟内存里,那么先把数据复制出来内存,如果内存没位置,得先把内存的某一空闲页先复制到硬盘,再把硬盘的复制到内存,再读写.
书里面写,慢,就是慢在了这个过程.
我相信楼主所担心的,也是担心这个过程.

我之前也测试过,不使用任何的硬盘虚拟内存,当我的物理内存数量不足时,仍然可以去生成一个1G的内存映射文件,当然,这个时候是不可以view整个文件的.其实这时从硬件上看,跟硬盘虚拟内存是一样的.当我们改变我们的view时,一定会是把之前view的东西先放好,如果内存没地方放的话,系统会先放入硬盘.要读取时再复制到内存.同样是一个硬盘到内存的复制过程.

楼主现在要减少内存的使用,你的软件就一定会增加更多的内存与硬盘数据交换的动作,慢是一定会的了,还是安心地用内存映射吧.
cnzdgs 2010-05-19
  • 打赏
  • 举报
回复
程序要处理数据肯定是在内存中进行的。如果用常规方式把文件全部读到内存中操作,需要经过文件系统缓冲,在物理内存充足时,文件系统缓冲不会立即释放,看起来占用内存较多,但不会影响系统效能;如果用文件映射则不经过文件系统缓冲,文件的读写由系统自动处理。
如果你需要把文件全部调到内存中处理,这两种方式几乎没有差别,要减少内存占用只能是分段处理。

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧