Java内存映射,上G大文件轻松处理

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qing_gee/article/details/99579341

内存映射文件(Memory-mapped File),指的是将一段虚拟内存逐字节映射于一个文件,使得应用程序处理文件如同访问主内存(但在真正使用到这些数据前却不会消耗物理内存,也不会有读写磁盘的操作),这要比直接文件读写快几个数量级。

稍微解释一下虚拟内存(很明显,不是物理内存),它是计算机系统内存管理的一种技术。像施了妖法一样使得应用程序认为它拥有连续的可用的内存,实际上呢,它通常是被分隔成多个物理内存的碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

内存映射文件主要的用处是增加 I/O 性能,特别是针对大文件。对于小文件,内存映射文件反而会导致碎片空间的浪费,因为内存映射总是要对齐页边界,最小单位是 4 KiB,一个 5 KiB 的文件将会映射占用 8 KiB 内存,也就会浪费 3 KiB 内存。

java.nio 包使得内存映射变得非常简单,其中的核心类叫做 MappedByteBuffer,字面意思为映射的字节缓冲区。

01、使用 MappedByteBuffer 读取文件

假设现在有一个文件,名叫 cmower.txt,里面的内容是:

沉默王二,一个有趣的程序员

PS:哎,改不了王婆卖瓜自卖自夸这个臭毛病了,因为文章被盗得都怕了。

这个文件放在 /resource 目录下,我们可以通过下面的方法获取到它:

ClassLoader classLoader = Cmower.class.getClassLoader();
Path path = Paths.get(classLoader.getResource("cmower.txt").getPath());

Path 既可以表示一个目录,也可以表示一个文件,就像 File 那样——当然了,Path 是用来取代 File 的。

然后,从文件中获取一个 channel(通道,对磁盘文件的一种抽象)。

FileChannel fileChannel = FileChannel.open(path);

紧接着,调用 FileChannel 类的 map 方法从 channel 中获取 MappedByteBuffer,此类扩展了 ByteBuffer——提供了一些内存映射文件的基本操作方法。

MappedByteBuffer mappedByteBuffer = fileChannel.map(mode, position, size);

稍微解释一下 map 方法的三个参数。

1)mode 为文件映射模式,分为三种:

  • MapMode.READ_ONLY(只读),任何试图修改缓冲区的操作将导致抛出 ReadOnlyBufferException 异常。

  • MapMode.READ_WRITE(读/写),任何对缓冲区的更改都会在某个时刻写入文件中。需要注意的是,其他映射同一个文件的程序可能不能立即看到这些修改,多个程序同时进行文件映射的行为依赖于操作系统。

  • MapMode.PRIVATE(私有), 对缓冲区的更改不会被写入到该文件,任何修改对这个缓冲区来说都是私有的。

2)position 为文件映射时的起始位置。

3)size 为要映射的区域的大小,必须是非负数,不得大于Integer.MAX_VALUE

一旦把文件映射到内存缓冲区,我们就可以把里面的数据读入到 CharBuffer 中并打印出来。具体的代码示例如下。

CharBuffer charBuffer = null;
ClassLoader classLoader = Cmower.class.getClassLoader();
Path path = Paths.get(classLoader.getResource("cmower.txt").getPath());
try (FileChannel fileChannel = FileChannel.open(path)) {
    MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0, fileChannel.size());
    
    if (mappedByteBuffer != null) {
        charBuffer = Charset.forName("UTF-8").decode(mappedByteBuffer);
    }
    
    System.out.println(charBuffer.toString());
} catch (IOException e) {
    e.printStackTrace();
}

由于 decode() 方法的参数是 MappedByteBuffer,这就意味着我们是从内存中而不是磁盘中读入的文件内容,所以速度会非常快。

02、使用 MappedByteBuffer 写入文件

假设现在要把下面的内容写入到一个文件,名叫 cmower1.txt。

沉默王二,《Web全栈开发进阶之路》作者

这个文件还没有创建,计划放在项目的 classpath 目录下。

 Path path = Paths.get("cmower1.txt");

具体位置见下图所示。

然后,创建文件的通道。

FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE,
                StandardOpenOption.TRUNCATE_EXISTING)

仍然使用的 open 方法,不过增加了 3 个参数,前 2 个很好理解,表示文件可读(READ)、可写(WRITE);第 3 个参数 TRUNCATE_EXISTING 的意思是如果文件已经存在,并且文件已经打开将要进行 WRITE 操作,则其长度被截断为 0。

紧接着,仍然调用 FileChannel 类的 map 方法从 channel 中获取 MappedByteBuffer。

 MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, 0, 1024);

这一次,我们把模式调整为 MapMode.READ_WRITE,并且指定文件大小为 1024,即 1KB 的大小。然后使用 MappedByteBuffer 中的 put() 方法将 CharBuffer 的内容保存到文件中。具体的代码示例如下。

CharBuffer charBuffer = CharBuffer.wrap("沉默王二,《Web全栈开发进阶之路》作者");

Path path = Paths.get("cmower1.txt");

try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE,
        StandardOpenOption.TRUNCATE_EXISTING)) {
    MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, 0, 1024);

    if (mappedByteBuffer != null) {
        mappedByteBuffer.put(Charset.forName("UTF-8").encode(charBuffer));
    }

} catch (IOException e) {
    e.printStackTrace();
}

可以打开 cmower1.txt 查看一下内容,确认预期的内容有没有写入成功。

03、MappedByteBuffer 的遗憾

据说,在 Java 中使用 MappedByteBuffer 是一件非常麻烦并且痛苦的事,主要表现有:

1)一次 map 的大小最好限制在 1.5G 左右,重复 map 会增加虚拟内存回收和重新分配的压力。也就是说,如果文件大小不确定的话,就不太友好。

2)虚拟内存由操作系统来决定什么时候刷新到磁盘,这个时间不太容易被程序控制。

3)MappedByteBuffer 的回收方式比较诡异。

再次强调,这三种说法都是据说,我暂时能力有限,也不能确定这种说法的准确性,很遗憾。

04、比较文件操作的处理时间

嗨,朋友,阅读完以上的内容之后,我想你一定对内存映射文件有了大致的了解。但我相信,如果你是一名负责任的程序员,你一定还想知道:内存映射文件的读取速度究竟有多快。

为了得出结论,我叫了另外三名竞赛的选手:InputStream(普通输入流)、BufferedInputStream(带缓冲的输入流)、RandomAccessFile(随机访问文件)。

读取的对象是加勒比海盗4惊涛怪浪.mkv,大小为 1.71G。

1)普通输入流

public static void inputStream(Path filename) {
    try (InputStream is = Files.newInputStream(filename)) {
        int c;
        while((c = is.read()) != -1) {
            
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2)带缓冲的输入流

public static void bufferedInputStream(Path filename) {
    try (InputStream is = new BufferedInputStream(Files.newInputStream(filename))) {
        int c;
        while((c = is.read()) != -1) {
            
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

3)随机访问文件

public static void randomAccessFile(Path filename) {
    try (RandomAccessFile randomAccessFile  = new RandomAccessFile(filename.toFile(), "r")) {
        for (long i = 0; i < randomAccessFile.length(); i++) {
            randomAccessFile.seek(i);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4)内存映射文件

public static void mappedFile(Path filename) {
    try (FileChannel fileChannel = FileChannel.open(filename)) {
        long size = fileChannel.size();
        MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0, size);
        for (int i = 0; i < size; i++) {
            mappedByteBuffer.get(i);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

测试程序也很简单,大致如下:

long start = System.currentTimeMillis();
bufferedInputStream(Paths.get("jialebi.mkv"));
long end = System.currentTimeMillis();
System.out.println(end-start);

四名选手的结果如下表所示。

方法 时间
普通输入流 龟速,没有耐心等出结果
随机访问文件 龟速,没有耐心等下去
带缓冲的输入流 29966
内存映射文件 914

普通输入流和随机访问文件都慢得要命,真的是龟速,我没有耐心等待出结果;带缓冲的输入流的表现还不错,但相比内存映射文件就逊色多了。由此得出的结论就是:内存映射文件,上G大文件轻松处理

05、最后

本篇文章主要介绍了 Java 的内存映射文件,MappedByteBuffer 是其灵魂,读取速度快如火箭。另外,所有这些示例和代码片段都可以在 GitHub 上找到——这是一个 Maven 项目,所以它很容易导入和运行。

欢迎关注「沉默王二」公众号,后台回复关键字「Java」获取 Java 常用算法手册——成为高手的必备手册。

扫码关注

展开阅读全文

高手请进,解决一个大文件处理内存映射问题,跪求~~

02-18

我做了一个数据处理的程序,可是在处理大文件数据时会因为内存不足而停止,rnrn有人告诉我可以用内存映射来处理,我是新手,跪求哪位大哥大姐,兄弟姐妹rnrn帮我一把,程序如下:rnrnunit zhcx;rnrninterfacernrnusesrn Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,rn Dialogs, StdCtrls, ExtCtrls, IniFiles;rnrntypern TForm1 = class(TForm)rn Button1: TButton;rn LB1: TListBox;rn GroupBox1: TGroupBox;rn CB1: TCheckBox;rn CB2: TCheckBox;rn CB3: TCheckBox;rn CB4: TCheckBox;rn CB5: TCheckBox;rn procedure Button1Click(Sender: TObject);rnrn privatern Private declarations rn publicrn Public declarations rn end;rnrnvarrn Form1: TForm1;rnrnimplementationrnrn$R *.dfmrnrnprocedure TForm1.Button1Click(Sender: TObject);rnvarrn t,temp,t1:TStringList;rn ASource,Alien,s:TstringList;rn i:Integer;rn AppIni: TIniFile;rn str1:string;rn str2:string;rnbeginrn AppIni := TInifile.Create('.\config.ini');rn // HYF processrn if FileExists('.\teldata\ffy_info_hff.txt') andrn CB1.Checked thenrn beginrn str1:=AppIni.ReadString('pos','HYF','');rn t:= TStringList.Create;rn t1 := TStringList.Create;rn temp:=TStringList.Create;rn temp.Delimiter:=';';rn t.LoadFromFile('.\teldata\ffy_info_hff.txt');rn // showmessage(str1);rn for i := 0 to t.Count - 1 dorn beginrn temp.DelimitedText:=t.Strings[i];rn t1.Add(Format('%20s;',[temp[3]])+rn Format('%20s;',[temp[5]])+' ;'+' HYF;'+rn Format('%15d;',[StrToInt(temp[1])* StrToInt(str1)div 100])+' ;'rn +Format('%20s;',[temp[6]])+' ;');rn end;rn t1.SaveToFile('.\teldata\deal\ffy_info_hff.txt');rn t.Free;rn t1.Free;rn temp.Free;rn // showMessage('HYF deal!');rn end;rn // CL-YFF processrn if FileExists('.\teldata\cai_ling_info_yff.txt') andrn CB2.Checked thenrn beginrn str1:=AppIni.ReadString('pre','CL','');rn str2:=AppIni.ReadString('rate','CL','');rn ASource := TStringList.Create;rn ALien := TStringList.Create;rn s:=TStringList.Create;rn ASource.LoadFromFile('.\teldata\cai_ling_info_yff.txt');rn for i := 0 to ASource.Count - 1 dorn beginrn alien.clear;rn ExtractStrings([' '], [],pchar(ASource.Strings[i]) , ALien);rn s.Add(Format('%20s;',[ALien.Strings[0]])+rn Format('%10s;',[ALien.Strings[8]])+' Y;'+' CL;'+rn Format('%15d;',[StrToInt(ALien.Strings[7])*StrToInt(str1)*StrToInt(str2) div 10000])rn +' ;'+Format('%20s;',[ALien.Strings[6]])+' ;');rn end;rn s.SaveToFile('.\teldata\deal\cai_ling_info_yff.txt');rn Asource.Free;rn Alien.Free;rn s.Free;rn // showMessage('CL-YFF deal!');rn end;rnrn //CL-HFF processrn if FileExists('.\teldata\cai_ling_info_hff.txt') andrn CB3.Checked thenrn beginrn str1:=AppIni.ReadString('pos','CL','');rn ASource := TStringList.Create;rn ALien := TStringList.Create;rn s:=TStringList.Create;rn ASource.LoadFromFile('.\teldata\cai_ling_info_hff.txt');rn for i := 0 to ASource.Count - 1 dorn beginrn alien.clear;rn ExtractStrings([' '], [],pchar(ASource.Strings[i]) , ALien);rn s.Add(Format('%20s;',[ALien.Strings[0]])+rn Format('%10s;',[ALien.Strings[8]])+' H;'+' CL;'+rn Format('%15d;',[StrToInt(ALien.Strings[7])*StrToInt(str1)div 100])+' ;'+rn Format('%20s;',[ALien.Strings[6]])+' ;');rn end;rn s.SaveToFile('.\teldata\deal\cai_ling_info_hff.txt');rn Asource.Free;rn Alien.Free;rn s.Free;rn // showMessage('CL-HFF deal!');rn end;rn //SPDX-YFFrn if FileExists('.\teldata\phs_sms_info_down_yff.txt') andrn CB4.Checked thenrn beginrn str1:=AppIni.ReadString('pre','DX','');rn str2:=AppIni.ReadString('rate','DX','');rn ASource := TStringList.Create;rn ALien := TStringList.Create;rn s:=TStringList.Create;rn ASource.LoadFromFile('.\teldata\phs_sms_info_down_yff.txt');rn for i := 0 to ASource.Count - 1 dorn beginrn alien.clear;rn ExtractStrings([' '], [],pchar(ASource.Strings[i]) , ALien);rn s.Add(Format('%20s;',[ALien.Strings[4]])+rn Format('%10s;',[ALien.Strings[5]])+' Y;'+' SPDX;'+rn Format('%15d;',[StrToInt(ALien.Strings[10])*StrToInt(str1)*StrToInt(str2) div 10000])+' ;'+rn Format('%20s;',[ALien.Strings[15]])+' ;');rn end;rn s.SaveToFile('.\teldata\deal\phs_sms_info_down_yff.txt');rn Asource.Free;rn Alien.Free;rn s.Free;rn // showMessage('SPDX-YFF deal!');rn end;rn //SPDX-HFFrn if FileExists('.\teldata\phs_sms_info_down_hff.txt')andrn CB5.Checked thenrn beginrn str1:=AppIni.ReadString('pos','DX','');rn ASource := TStringList.Create;rn ALien := TStringList.Create;rn s:=TStringList.Create;rn ASource.LoadFromFile('.\teldata\phs_sms_info_down_hff.txt');rn for i := 0 to ASource.Count - 1 dorn beginrn alien.clear;rn ExtractStrings([' '], [],pchar(ASource.Strings[i]) , ALien);rn s.Add(Format('%20s;',[ALien.Strings[4]])+rn Format('%10s;',[ALien.Strings[5]])+' H;'+' SPDX;'+rn Format('%15d;',[StrToInt(ALien.Strings[10])*StrToInt(str1) div 100])+' ;'+rn Format('%20s;',[ALien.Strings[15]])+' ;');rn end;rn s.SaveToFile('.\teldata\deal\phs_sms_info_down_hff.txt');rn Asource.Free;rn Alien.Free;rn s.Free;rn // showMessage('SPDX-HFF deal!');rn end;rnend;rnrnrnend. 论坛

内存映射 处理大txt

02-15

我对一个txt进行内存映射后,想把文件内容读到CString里然后进行处理,请问怎么读;rn或者是我思路错了,不能读到CString里应该直接处理,可是应该怎么处理呢,比如排序后删除重复的。比如查找某个字符rn西面是代码[code=C/C++]rnCFileDialog fileDlg(TRUE, "*.txt", "*.txt", NULL, "文本文件 (*.txt)|*.txt||", this);rnfileDlg.m_ofn.Flags |= OFN_FILEMUSTEXIST;rnfileDlg.m_ofn.lpstrTitle = "通过内存映射文件读取数据";rnif (fileDlg.DoModal() == IDOK)rnrn//创建文件对象rnHANDLE hFile = CreateFile(fileDlg.GetPathName(), GENERIC_READ | GENERIC_WRITE,rn0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);rnif (hFile == INVALID_HANDLE_VALUE)rnrnTRACE("创建文件对象失败,错误代码:%drn", GetLastError());rnreturn;rnrn//创建文件映射对象rnHANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);rnif (hFileMap == NULL)rnrnTRACE("创建文件映射对象失败,错误代码:%drn", GetLastError());rnreturn;rnrn//得到系统分配粒度rnSYSTEM_INFO SysInfo;rnGetSystemInfo(&SysInfo);rnDWORD dwGran = SysInfo.dwAllocationGranularity;rn//得到文件尺寸rnDWORD dwFileSizeHigh;rn__int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);rnqwFileSize |= (((__int64)dwFileSizeHigh) << 32);rn//关闭文件对象rnCloseHandle(hFile);rn//偏移地址 rn__int64 qwFileOffset = 0;rn//块大小rnDWORD dwBlockBytes = 1000 * dwGran;rnif (qwFileSize < 1000 * dwGran)rndwBlockBytes = (DWORD)qwFileSize;rnwhile (qwFileOffset > 0)rnrn//映射视图rnLPBYTE lpbMapAddress = (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, rn(DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),rndwBlockBytes);rnif (lpbMapAddress == NULL)rnrnTRACE("映射文件映射失败,错误代码:%drn", GetLastError());rnreturn;rnrn//对映射的视图进行访问rnfor(DWORD i = 0; i < dwBlockBytes; i++)rnBYTE temp = *(lpbMapAddress + i);rnrn//撤消文件映像rnUnmapViewOfFile(lpbMapAddress);rn//修正参数rnqwFileOffset += dwBlockBytes;rnqwFileSize -= dwBlockBytes;rnrn//关闭文件映射对象句柄rnCloseHandle(hFileMap);rnAfxMessageBox("成功完成对文件的访问");rnrnrnrn[/code] 论坛

VC++中使用内存映射文件处理大文件

01-29

摘要rn rn  本文给出了一种方便实用的解决大文件的读取、存储等处理的方法,并结合相关程序代码对具体的实现过程进行了介绍。 rnrn  引言 rnrn  文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种Windows核心编程技术展开讨论。 rnrn  内存映射文件 rnrn  内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。 rnrn  内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。下面给出使用内存映射文件的一般方法: rnrn  首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。 rnrn  内存映射文件相关函数 rnrn  在使用内存映射文件时,所使用的API函数主要就是前面提到过的那几个函数,下面分别对其进行介绍: rnrn  HANDLE CreateFile(LPCTSTR lpFileName,rn  DWORD dwDesiredAccess,rn  DWORD dwShareMode,rn  LPSECURITY_ATTRIBUTES lpSecurityAttributes,rn  DWORD dwCreationDisposition,rn  DWORD dwFlagsAndAttributes, rn  HANDLE hTemplateFile);rn rn  函数CreateFile()即使是在普通的文件操作时也经常用来创建、打开文件,在处理内存映射文件时,该函数来创建/打开一个文件内核对象,并将其句柄返回,在调用该函数时需要根据是否需要数据读写和文件的共享方式来设置参数dwDesiredAccess和dwShareMode,错误的参数设置将会导致相应操作时的失败。 rnrn  HANDLE CreateFileMapping(HANDLE hFile,rn  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,rn  DWORD flProtect,rn  DWORD dwMaximumSizeHigh,rn  DWORD dwMaximumSizeLow,rn  LPCTSTR lpName);rn rn  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,几乎可以满足任何大数据量文件处理场合的要求。 rnrn 论坛

在虚拟地址空间足够的情况下内存映射大文件失败

01-22

我想映射一个大小900多M的文件,CreateFileMapping能够返回有效句柄,但MapViewOfFile却失败返回0,错误代码是8,存储空间不足 无法处理此命令。但是我用GlobalMemoryStatus查看了一下,可用地址空间还有接近2G呢。也就是说地址空间是足够的。请问还有别的原因能够导致失败吗?下面是我的代码rn[code=C/C++][/code]rn hFile=CreateFile(pcszDicName, rn GENERIC_READ, rn FILE_SHARE_READ, rn NULL, rn OPEN_EXISTING, rn FILE_ATTRIBUTE_NORMAL, rn NULL);rn if(hFile == INVALID_HANDLE_VALUE) exit(0);rn hFilemap = CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,"TTS");rn rn rnrn MEMORYSTATUS ms = sizeof(ms);rn GlobalMemoryStatus(&ms);rn DWORD availVirtual = ms.dwAvailVirtual;rn FILE* flog = fopen("d:\\log","a");rn fprintf(flog," availVirtual:%d\n",availVirtual);rn fclose(flog);rnrn void* pVoid=MapViewOfFile(hFilemap,FILE_MAP_READ,0,0,0);rn dwe = GetLastError();rn if(pVoid == NULL)rn rn MEMORYSTATUS ms = sizeof(ms);rn GlobalMemoryStatus(&ms);rn DWORD availVirtual = ms.dwAvailVirtual;rn FILE* flog = fopen("d:\\log","a");rn fprintf(flog,"hFilemap = CreateFileMapping failed\n");rn fprintf(flog,"error:%d",dwe);fprintf(flog," availVirtual:%d\n",availVirtual);rn fclose(flog);rn printf("mapping failed£¬lack of memory");rn exit(0);rn rn[code=C/C++][/code]rn这是log文件信息:rnavailVirtual:2068520960rnhFilemap = CreateFileMapping failedrnerror:8 availVirtual:2068520960 论坛

没有更多推荐了,返回首页