最近一个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
证明二维化正确