现象:XP环境下,在自己写的程序中,用::CreateFile()和::ReadFile()函数以二进制模式顺序读取一较大文件(约500M)左右,频繁顺序读取不超过1M的数据进入同一块内存,直至文件结束。奇怪现象:在读取过程中,通过任务管理器发现总的可用物理内存一直持续下降,系统缓存轻微持续上涨,但是任务列表中没有任何程序的内存持续上涨,包括我自己的进程(约占20M),都稳定在一个水平,当最终文件读取完毕时,可用物理内存已经降低至非常少了(只有约60-70M,总共512M,程序执行前约300M空闲),此时系统性能明显下降,关闭文件后,可用物理内存数量恢复,系统性能也恢复。问题:
::ReadFile()读取文件时,Windows会缓存所有已经读取的数据吗?如果不是,Windows会缓存多少数据?如何使Windows只缓存少量数据或者不缓存数据,从而达到操作大型文件时也能保持较多的物理内存和较好的系统性能?望高手不吝赐教和讨论,若觅得答案,分数一定及时奉上,谢谢!

解决方案 »

  1.   

    CreateFile时用FILE_FLAG_NO_BUFFERING标志啊
    联合使用FILE_FLAG_OVERLAPPED更好
      

  2.   

    楼主说的现象好象是系统没有及时释放使用的内存和缓存,使得系统性能下降两个建议 
    1、跟踪直 CReadFile 内查看 MFC 代码,看其是怎样使用内存的
    2、查看有无手工释放无用内存(缓存)的API
      

  3.   

    我试验了一下操作一个几十兆的文件,我就是一遍遍不停的反复连续小块儿地读取该文件,发现一开始可用内存也是持续下降,当下降了几十兆以后就不再下降了,这个过程只要了很短的一段时间,之后我让程序继续运行了半个多小时,可用物理内存一直稳定在那个水平,此时硬盘指示灯也几乎不闪烁,基本可以推断:Windows已经在物理内存中完全缓存了该文件,因为硬盘的缓存没有那么大。得出一个初步结论:如果一个程序频繁地读取某个文件的话,Windows会尝试使用缓存的方式来优化这些读取操作,如果如此频繁读取文件的持续时间足够长,并且读取的区域涉及整个文件的话,Windows甚至会尝试将整个文件用物理内存来缓存起来(也许它还会考虑使用虚拟缓存),这也许就是导致我操作大文件时,物理内存消耗光的原因吧。不知道各位是否认同我的这个分析?
      

  4.   

    to 38062708:我没有使用 MFC 或任何其它类,我是直接使用的::ReadFile()这个Win32 API函数。to xiaoqiqixiao:加上 FILE_FLAG_NO_BUFFERING 这个标志的话,就对读取的尺寸有严格限制,挺麻烦的,如果找不到其它更合适的方案的话,才会考虑此方法。另外,我也想讨论并证实一下Windows的文件缓存机制到底是怎样的。
      

  5.   

    Win32 API:FlushFileBuffers() 只能对写操作有效,功能是强迫写入物理磁盘。但是对于读取操作没用啊!
      

  6.   

    用内存映射文件来访问:
    class CEosFontData
    {
    public:
    public:
    BYTE *m_pFileMap;
    protected:
    HANDLE m_FileHandle;
    HANDLE m_MapHanle;
    public:
    BOOL OpenFile(CString strPathName);
    void SetCharData();
    BOOL CloseFile();
    };
    BOOL CEosFontData::OpenFile(CString strPathName)
    {
    m_FileHandle = CreateFile(strPathName,GENERIC_READ,FILE_SHARE_READ,
    0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
    if(m_FileHandle == INVALID_HANDLE_VALUE)
    return FALSE;
    m_MapHanle = CreateFileMapping(m_FileHandle,0,PAGE_READONLY,0,0,0);
    if(!m_MapHanle)
    return FALSE;
    m_pFileMap = (BYTE*)MapViewOfFile(m_MapHanle,FILE_MAP_READ,0,0,0);
    if(!m_pFileMap)
    return FALSE;
    return TRUE;
    }
    BOOL CEosFontData::CloseFile()
    {
    if(!UnmapViewOfFile(m_pFileMap))
    return FALSE;
    CloseHandle(m_FileHandle);
    CloseHandle(m_MapHanle);
    return TRUE;
    }利用上面的打开/关闭文件,打开后用m_pFileMap一次访问文件内容就可以了,跳过内容直接用指针的移动。
      

  7.   

    用VirtualAlloc()和VirtualFree()自己提交、回收物理存储器不知效果如何?
      

  8.   

    to laiyiling:
    用内存映射文件的方式的话,Windows就不会缓存了吗?这是为什么呢?to Snow_Ice11111(雪上加冰):
    我只在开始分配了一小块内存,然后所有读取的数据都放到这块内存中,每次都覆盖,最后才释放。Windows自己缓存文件根VirtualAlloc()和VirtualFree()函数有什么关系呀?
      

  9.   

    windows的内存分配最后一般都要用到VirtualAlloc()
      

  10.   

    to freemme(路在脚下):那我有办法控制它的VirtualAlloc()和VirtualFree()吗?
      

  11.   

    其实我也并不是想禁止Windows进行缓存,而是希望Windows只做一点点就行了,而不会自动耗尽所有内存去缓存一个大文件,适得其反。
      

  12.   

    大型文件可以考虑file mapping,这个效率比较高
      

  13.   

    to oyljerry(【勇敢的心】→ ㊣如果·爱√㊣):谢谢你的建议,不过我想在关心的不是读写本身的效率问题,而是当Windows为了缓存文件导致物理内存消耗太多而产生的系统效率问题。没办法了,我只好考虑加FILE_FLAG_NO_BUFFERING标志试试看了。
      

  14.   

    痛苦啊,要读写 FILE_FLAG_NO_BUFFERING 标志打开的文件真是麻烦啊,到最后还不知道行不行,555
      

  15.   

    苦思一夜,再结合大家的意见,我有了如下不太成熟的浅见,欢迎大家批评指正:
      CreateFile()函数使用时,系统不会立即把文件的所有内容读进内存中,不过当你读取其中的部分数据后,为了加快你再次访问这块数据的速度,它会把这块数据缓存在物理存储器中(这里没用内存这个词是因为系统可能把它放在磁盘的页文件中),这时程序所占用的内存和你存放数据所用的变量占用的内存无关,所以即使用VirtualAlloc()和VirtualFree()自己管理内存的使用和回收也不会有什么改善。回到你的问题中来,如果想避免程序占用资源过多,你试着用CloseHandle()关闭该文件,然后再次打开看效果如何。BTW: 我现在在用的visual-studio-booster就是你在CSDN发的散分贴时送的注册号,很好用!再次感谢一下!!
      

  16.   

    oyljerry(【勇敢的心】→ ㊣如果·爱√㊣)朋友的建议很好,读写大文件时用内存映射方法再合适不过了,不光因为这种方法效率高,更因为你可以用很少的内存和硬盘资源就可以完成同样的工作,我会首先考虑这个方法的。上面回复也仅是从研究的角度来用考虑的。
      

  17.   

    首先,十分感谢各位的热心关注!以及感谢Snow_Ice11111(雪上加冰)同志对我的作品的支持!经过奋战(昨夜写程序志凌晨3点半),加 FILE_FLAG_NO_BUFFERING 的文件读写操作已经完成,事实证明:那剧烈的内存消耗的罪魁祸首的确是 Windows 的文件缓存机制,不过我在实现自己控制文件缓存的过程中也发现,因为 Windows 必须考虑通用性,所以也只能采取它现有的文件缓存方式,也就是说,在一直打开一个大文件进行::ReadFile()或::WriteFile()操作时,可用物理内存逐步耗尽是必然的,也就是说,也不怪 Microsoft 了 ^_^。所以,因为我程序的需要,我牺牲了一点通用性来实现了 FILE_FLAG_NO_BUFFERING 的非缓冲文件读写(只用了一点点缓冲),从而实现了我程序本身所消耗的实际物理内存和任务管理器中看到的占用内存数基本一致,大约20M,而以前则是任务管理器中看到只占20M,但实际物理内存会直线下降到非常低,甚至会导致机器非常卡(系统频繁交换页面)。此外,Snow_Ice11111(雪上加冰)的见解十分正确,不过还要提示一点,就是经过我的研究,发现 Windows 作文件缓存目的之一固然是为了提高访问速度(靠Cache命中),但我要说的是,Windows 也被迫必须做缓存,这是由于磁盘文件读写是基于扇区为单位的,而不是以字节为单位的,要支持文件指针的任意移动,所以一定得进行缓存,关于具体细节,请参见 FILE_FLAG_NO_BUFFERING 标志的 MSDN 说明以及推理分析。还有,建议用文件映射的朋友们的意见也不错,不过我还没有实际去验证。我之所以没有采纳这样的意见,是因为我的程序还需要写大文件,基于上述分析,写大文件也会导致物理内存的大量消耗,而内存映射文件无法支持可变长文件,所以我只好统一用 FILE_FLAG_NO_BUFFERING 的方法去实现读写操作了,并且已经基本实现,而且可以达到预期效果。至于源代码,由于跟我的程序结合的比较紧密,特别是缓冲区的控制是为我的程序定制的算法,所以无法贴出来跟大家共享,不好意思。不过有一点可以提示大家,只要根据 MSDN 上对 FILE_FLAG_NO_BUFFERING 标志的要求说明,结合恰当的缓冲区管理算法,实现起来也不难,而且真的可以节省大量物理内存喔^_^