一种通过linux特性mmap节约内存的思想

最近一个JNI调用的库,需要一口气要一个高达1GB占用的文件放入一个数组去进行数据处理,此时如果手机的内存不足的话很容易导致我们的APP在使用该功能时OOM,但是这个第三方库我没有源码改不了,所以想了一下自己学过的知识,突然想到以前学Linux C编程的时候,可以用mmap把外存文件映射为一个内存地址,这样就可以不实际占用内存的情况下读取这个文件了,于是写了一个demo测试一下:

#include "stdio.h"
#include "stdlib.h"
#include
#include
#include

int main() {
    int f = open("/media/chenjiezhu/doc/迅雷下载/cn_windows_server_2012_r2_with_update_x64_dvd_6052725.iso", O_RDWR);
    long fileSize = 5545705472;
    //获得磁盘文件的内存映射
    char *mapped = (char *) mmap(0 , fileSize, PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
    long i;
    for(long i = fileSize - 10000; i < fileSize; i++) {
        printf("%d", *(mapped + i));
    }
    return 0;
}

我加载了一个windows_server的镜像作为大文件读写测试,看看内存占用:

 PID  USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND   
 7399 chenjie+  20   0 5419912   8808   8716 S  22.8  0.0   0:01.48 test1.bin  

 

按照物理内存占用公式RES - SHR = 8808 - 8716 = 92byte,实际占用内存才这么点,但可以实现和加载了整个大文件在内存里面使用的效果,那我们使用那个第三方库时,完全可以把数组数据怼进文件里面,再mmap出一个地址,让第三方库直接使用这个地址去取数据即可缓解内存压力。

 

封装一下,再依赖一些指针的奇技淫巧,还可以把映射出来的指针当二维数组读写

/**@author 陈杰柱**/

#include "stdio.h"
#include "stdlib.h"
#include 
#include 
#include 
#include 
#include 

typedef struct fileMapItem {
    int fileHandle;
    void* fileMapAddress;  
    long fileSize;
} FileMapItem;
FileMapItem getFileMapAddress(char* filePath);
void endFileMap(FileMapItem fileMapItem);

int main() {
    FileMapItem f = getFileMapAddress("./testReadFile.txt");
    int i, j;
    char **p = malloc(2 * sizeof(char*));
    *p = (char*) f.fileMapAddress;
    *(p + 1) = (char*) (f.fileMapAddress + 3);
    for(i = 0; i < 2; i++) {
        for(j = 0; j < 3; j++) {
            printf("i = %d, j = %d, c = %c\n", i, j, p[i][j]);
        }    
    }
    endFileMap(f);
    free(p);
    return 0;
}

/*Map a file to memory to relieve memory pressure.
It won't be loading files into memory, 
just make it look like it's in memory and operate like it's in memory*/
FileMapItem getFileMapAddress(char* filePath) {
    struct stat buf;  
    FileMapItem fItem;
    //give file size
    if(stat(filePath, &buf) < 0) {  
        fItem.fileHandle = -1;
        fItem.fileMapAddress = (void*) -1;
        return fItem;  
    }  
    long fileSize = buf.st_size;  
    int f = open(filePath, O_RDWR);
    //map the file start position as a FAKE memory address.
    void* fileMapAddress = mmap(0 , fileSize, PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
    fItem.fileSize = fileSize;
    fItem.fileHandle = f;
    fItem.fileMapAddress = fileMapAddress;
    return fItem;
}

/*close the file map */
void endFileMap(FileMapItem fileMapItem) {
    munmap(fileMapItem.fileMapAddress, fileMapItem.fileSize);
    close(fileMapItem.fileHandle);
}

文件内容就是0123456789,所以输出的结果是:

i = 0, j = 0, c = 0
i = 0, j = 1, c = 1
i = 0, j = 2, c = 2
i = 1, j = 0, c = 3
i = 1, j = 1, c = 4
i = 1, j = 2, c = 5

证明二维化正确

你可能感兴趣的:(C语言)