通过代码理解linux两种内存分配方式

之前在Linux平台开发时遇到过内存问题,当时查看过linux的内存分配原理,总结下来主要就是Linux有两种内存分配方式:

  1. 当申请的内存小于 128K 时,会通过 brk() 系统调用,从堆区分配内存。malloc和free时只是移动brk堆顶指针,free后不会将内存立即归还操作系统,只有当堆顶存在大于128K的空闲内存,才会将内存真正释放。
  2. 当申请的内存大于 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是如何分配内存的?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值