背景介绍
现在有35块内存,每块200M,均采用malloc分配。
在使用中,他们都被填入了10M~100M不等的数据,余下部分空闲。
然后我们将这35块内存中的有数据部分复制到一块1G的大内存中(已知有效数据总和不超过1G)。
伪代码如下
#define MAX_PER_BLOCK_MEM_SIZE (1024*1024*200)
#define MAX_TOTAL_BLOCK_MEM_SIZE (1024*1024*1024)
for ( i = 0; i < 35; i++)
mem[i] = malloc(MAX_PER_BLOCK_MEM_SIZE);
big_block = malloc(MAX_TOTAL_BLOCK_MEM_SIZE);
for ( i = 0; i < 35; i++)
put some data to mem[i];
len[i] = len(somedata);
for ( i = 0; i < 35; i++)
copyto(big_block, mem[i], len[i]);
这个段程序的执行结果是怎样的呢?由于总数据不足1G,按照一般机器的内存拷贝速度,预计总时间不超过1秒钟。但实际执行结果却让我费解,着1G不到的内存复制居然消耗了30秒!一般有两种情况会影响速度:
1、 Cache Miss
2、 Page Swap
鉴于速度下降如此厉害,Cache Miss的可能性不大。即使从Cache理论上分析,复制数据的过程与Cache命中率基本上无关---Cahce Read 的 Miss Rate必然接近100% ;对于Cache Write, Write-Back Cache可以一定程度上加速写内存的过程,但数据量一旦大大超过Cache Capacity,也会挂掉。 Write Through的Cache就不说了。所以应该是Page Swap造成的。
free查看空闲内存,约8G。 再算我申请的内存:35*0.2G + 1G = 8G 。居然是刚刚好……肯定是发生Page Swap了~!
系统空闲内存:
解决方法
将每块内存改小:
#define MAX_PER_BLOCK_MEM_SIZE (1024*1024*120)
重测,biu~~~秒杀。果然是发生换页了!
根据对操作系统内存分配机制的知识,这背后的机制却让我有些费解。
假设如下场景: malloc函数一次性分配了8G虚拟内存,而只实际只使用了其中的8G中的前面500M。
操作系统背后会做些什么呢?首先,为该进程分配映射8G空间所需要的页表,约占2M空间(按照PAGE_SIZE = 4KB算)。页表中填入什么呢?有两种选择:
立即分配(immediate allocation)方式: 只要有空闲物理内存页,就立即将这些空闲页分配给进程,页地址填入到页表中。
懒惰分配(lazy allocation)方式: 不管系统中有不有空闲物理页,页表中先什么都不填,仅仅标志NOT_PRESENT。当用户用虚拟地址访问到该页面时,将发生缺页中断。操作系统此时根据系统维护的状态可以知道该次访问是对页面的第一次访问,需要为其分配物理页面。now,操作系统向物理页面管理器发起物理页面申请,并将申请到的页地址填入到页表中,用户就可以继续对页面的访问了。
两种方式各自有何利弊呢?立即分配的方式的弊端由我上面的程序就已经可以看出来了。当用户喜欢预先申请大量内存,却不及时释放的时候,系统性能将受到严重影响。比如,A进程一次申请了8G内存,B进程如果想申请2G内存,则没有可用物理页了。只好将页表标记为NOT_PRESENT。一旦B进程访问这2G内存,将发生缺页中断。由于系统中没有可用物理内存了(A进程在占着茅坑不拉屎@@),操作系统换页机制将启动,当前某些物理页将被换入到磁盘中。这个过程相当耗时!
对于懒惰分配方式,上面的情况将不会出现,因为A进程分配的8G内存并没有实际分配,系统内还是有8G的空闲物理页。懒惰方式的缺点在于,缺页中断将会频繁发生!陷入内核的开销也是不容小觑的。举个例子:某个进程A,分配了1G内存,由于采用了懒惰分配方式,顺序访问这1G内存将会导致1G/4K=256K次缺页中断。假设每次中断处理时间需要1us,那么缺页造成的损失为1us * 256K = 256ms = 0.2s,在性能很重要的计算环境下,这个开销已经很大了。
解决方案
系统进行内存分配的时候,区别对待大内存分配和小内存分配。对于大内存分配采用懒惰分配方式,对于小内存,则一次性满足。在采用懒惰分配方式的时候,每次发生了缺页,可以一次分配几十到几百个页面给进程,这样可以有效避免懒惰分配方式中存在的缺页开销大的问题。
结论
莫非linux犯傻了?回头看看源码去~
Note:
这里“缺页中断”有两种语义,请注意区别:
1、传统意义的缺页中断。内核需要到磁盘上去读回所缺页。
2、物理页层次的缺页中断。此时实际上有足够的物理页面,只不过这些物理页面没有分配给进程而已。只有当发生物理层次的缺页中断时这些物理页才分配给进程。
标签:善待,Cache,8G,内存,1G,缺页,分配 From: https://blog.51cto.com/u_16162111/6486707