之前在Linux平台开发时遇到过内存问题,当时查看过linux的内存分配原理,总结下来主要就是Linux有两种内存分配方式:
- 当申请的内存小于 128K 时,会通过 brk() 系统调用,从堆区分配内存。malloc和free时只是移动brk堆顶指针,free后不会将内存立即归还操作系统,只有当堆顶存在大于128K的空闲内存,才会将内存真正释放。
- 当申请的内存大于 128K 时,会通过 mmap() 系统调用,从文件映射区分配内存。mmap分配的内存在free的时候,会立即归还操作系统
这里的128k可以通过函数mallopt(M_MMAP_THRESHOLD, 128* 1024);
来设置自定义的值
以下通过代码分别验证brk和mmap两种内存分配方式中,free之后的内存变化。分别通过两种方式分配5G左右的内存,然后去free,打印过程中的内存值。通过以下Result可以验证上述结论。
Result
For brk
[root@stream04 testMemory]# g++ memory.cpp -o mem
You have mail in /var/spool/mail/root
[root@stream04 testMemory]# ./mem
testMemoryBrk: before allocate: VM: 13716; RSS: 1072
testMemoryBrk: after allocate: VM: 5.29341e+06; RSS: 5.275e+06
testMemoryBrk: after free: VM: 5.27702e+06; RSS: 5.26475e+06 //free 后内存无显著变化
testMemoryBrk: after trim: VM: 5.27697e+06; RSS: 1384
testMemoryBrk: done
For mmap
[root@stream04 testMemory]# g++ memory.cpp -o mem
[root@stream04 testMemory]# ./mem
testMemoryMmmap: before allocate: VM: 13716; RSS: 1072
testMemoryMmmap: after allocate: VM: 5.27708e+06; RSS: 5.26468e+06
testMemoryMmmap: after free: VM: 14748; RSS: 2500 //free 后内存显著降低
testMemoryMmmap: after trim: VM: 14756; RSS: 2400
testMemoryMmmap: done
Code
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <malloc.h>
#include <ios>
#include <fstream>
#include <string>
#include <cstring>
using namespace std;
int* srcData4K = NULL;
int* srcData1M = NULL;
void process_mem_usage(double& vm_usage, double& resident_set);
void testMemoryBrk()//brk方式的内存分配和释放
{
double vm, rss;
process_mem_usage(vm, rss);
cout << "testMemoryBrk: before allocate: VM: " << vm << "; RSS: " << rss << endl;
{
vector<int*> arrays;
for (int i = 0; i < 1310720; i++)
{
int num = 4*1024; //每次分配4k内存
int* ptr = (int*)malloc(num);
memcpy(ptr, srcData4K, 4*1024);//这里加个内存拷贝是为了触发系统将虚拟内存映射为物理内存
arrays.push_back(ptr);
}
process_mem_usage(vm, rss);
cout << "testMemoryBrk: after allocate: VM: " << vm << "; RSS: " << rss << endl;
for (int i = 0; i < 1310719; i++)
{
free(arrays[i]);
}
arrays.clear();
}
process_mem_usage(vm, rss);
cout << "testMemoryBrk: after free: VM: " << vm << "; RSS: " << rss << endl;
malloc_trim(0);
process_mem_usage(vm, rss);
cout << "testMemoryBrk: after trim: VM: " << vm << "; RSS: " << rss << endl;
printf("testMemoryBrk: done\n");
}
void testMemoryMmmap()//mmap方式的内存分配和释放
{
double vm, rss;
process_mem_usage(vm, rss);
cout << "testMemoryMmmap: before allocate: VM: " << vm << "; RSS: " << rss << endl;
{
vector<int*> arrays;
for (int i = 0; i < 5120; i++)
{
int num = 1*1024*1024; //每次分配1M内存
int* ptr = (int*)malloc(num);
memcpy(ptr, srcData1M, 1*1024*1024);//这里加个内存拷贝是为了触发系统将虚拟内存映射为物理内存
arrays.push_back(ptr);
process_mem_usage(vm, rss);
}
process_mem_usage(vm, rss);
cout << "testMemoryMmmap: after allocate: VM: " << vm << "; RSS: " << rss << endl;
for (int i = 0; i < 5119; i++)
{
free(arrays[i]);
}
arrays.clear();
}
process_mem_usage(vm, rss);
cout << "testMemoryMmmap: after free: VM: " << vm << "; RSS: " << rss << endl;
malloc_trim(0);
process_mem_usage(vm, rss);
cout << "testMemoryMmmap: after trim: VM: " << vm << "; RSS: " << rss << endl;
printf("testMemoryMmmap: done\n");
}
void process_mem_usage(double& vm_usage, double& resident_set)
{
using std::ios_base;
using std::ifstream;
using std::string;
vm_usage = 0.0;
resident_set = 0.0;
// 'file' stat seems to give the most reliable results
ifstream stat_stream("/proc/self/stat",ios_base::in);
// dummy vars for leading entries in stat that we don't care about
string pid, comm, state, ppid, pgrp, session, tty_nr;
string tpgid, flags, minflt, cminflt, majflt, cmajflt;
string utime, stime, cutime, cstime, priority, nice;
string O, itrealvalue, starttime;
// the two fields we want
unsigned long vsize;
long rss;
stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr
>> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt
>> utime >> stime >> cutime >> cstime >> priority >> nice
>> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest
stat_stream.close();
long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
vm_usage = vsize / 1024.0;
resident_set = rss * page_size_kb;
}
int main()
{
mallopt(M_MMAP_THRESHOLD, 128* 1024);
srcData4K = (int*)malloc(4*1024);
srcData1M = (int*)malloc(1*1024*1024);
//testMemoryBrk();
testMemoryMmmap();
free(srcData4K);
free(srcData1M);
return 0;
}
注:
malloc()分配的是虚拟内存。
如果分配后的虚拟内存没有被访问的话,是不会将虚拟内存映射到物理内存的,这样就不会占用物理内存了。
只有在访问已分配的虚拟地址空间的时候,操作系统通过查找页表,发现虚拟内存对应的页没有在物理内存中,就会触发中断,然后操作系统会建立虚拟内存和物理内存之间的映射关系。
Reference
How to get memory usage at runtime using C++?
malloc分配内存的方式
【操作系统】malloc是如何分配内存的?