转载地址:http://blog.youkuaiyun.com/luansxx/article/details/7702030
操作系统的虚拟内存分配表
OP显示的占用内存是进程的虚拟内存,我们需要了解的是程序实际占用内存的量,也就是物理内存占用。
虚拟内存的地址和实际物理内存的地址之间有对应关系,这个关系是操作系统维护的,CPU负责根据这个对应关系进行地址转换。这种对应关系标准的说法叫“内存映射”。
内存映射以页面映射形式实现。页面是固定大小的地址空间(即虚拟范围),虚拟内存的一个页面映射到物理内存的一个页面。没有必要假设连续的虚拟页面映射到连续的物理页面,即使物理页面不连续,只要虚拟页面连续,必要内存的一段地地址拟地己的虚拟地址空间(即虚拟地址范围),因为程序看到的就是连续的数据,这中间就是地址转换在起作用。
内存映射是一种复杂的关系,有下面的情形。
1. 虚拟页面没有映射物理页面
2. 虚拟页面映射某个物理页面
3. 多个虚拟页面映射同一个物理页面
当然不可能一个虚拟页面映射到多个物理页面。
虚拟内存是进程自身的,每个进程有自己的虚拟地址空间(即虚拟地址范围),虚拟地址空间独立,也就是每个进程都是独立的4G地址空间,但是实际物理内存很难有这么多(特别是嵌入式平台),所以虚拟地址空间可以看成是肯定足够的。
因为虚拟地址空间很大,所以宁可浪费虚拟地址空间也要节约物理内存。对于一个进程来说,虚拟内存使用量与物理内存使用量的差别源于上面的第1、3种映射情形。第2种情形两者是一对一映射,所以数量上一样。
我们再看看第1、3种映射情形的实际应用。
对于第1种情形,虚拟页面没有映射物理页面,一般发生在下列场合:
1. 程序的部分代码,因为某些条件未满足,目前还没有执行。
操作系统采用延迟加载策略,没有访问到的虚拟地址默认没有映射到物理内存,是无效的;但是一旦被执行,就会分配一个物理页面,从程序文件中加载数据(代码也是二进制数据)到该物理页面,并修改映射关系,使虚拟地址生效。另外如果某些代码比较长时间没有被执行(操作系统记录页面的空闲时间),操作系统会解除映射,释放物理内存,因为内存上的数据以后还可以再加载。
2. 程序的静态数据,因为某些条件未满足,还没有被访问到
程序的静态数据也是在程序文件中的,包括全局变量、字符串和编译器产生的静态表。与上面的原理一样,操作系统采用延迟加载策略和回收策略(只针对没有修改过的内存,除非有交换分区,这时可以保存在交换分区中)。
3. 堆中的空洞
程序的堆是程序动态申请释放内存所用的内存池。堆可以向上增长,也会退回。但是必须靠后的内存都释放了,而且堆的大小超过一定量(很多系统是128K)才退回。如果程序释放了前面或者中间的内存,那是不会退回的,这就形成了空洞。空洞映射物的理内存实际上是已经释放了,所以空洞没有映射物理内存。
4. 栈顶保留地址空间
程序的栈是函数一层层调用形式的,函数一层层调用的返回地址记录在栈中,函数调用的参数、函数内部的局部变量都存放在栈中。随着函数返回,局部变量、参数等都被释放,栈占用的内存又回退回来。由于难以估计函数调用层次最大会有多深,所以栈会多保留一些虚拟地址空间,这些虚拟地址空间也采用延迟映射的策略和回收策略(退回的数据肯定没有用了)。
5. 栈顶保护地址空间
如果程序有行为异常,对栈的使用超过了配置量,应该尽快发现,方法是在栈顶端后面额外加上小块(一般一个页面)虚拟地址空间,该地址没有任何权限(不能读写),一旦读写这块内存,操作系统会直接给进程发信号杀死进程。
对于第2种情形,多个虚拟页面映射同一个物理页面,一般发生在下列场合:
1. 多个进程共用同一份代码
这种情形一般发生的使用共享库的时候,还有就是同一个程序启动了多次。因为代码可以共用,所以同一份代码在物理内存中只会有一份,多个进程的虚拟地址都映射到同一个物理地址上。
2. 多个进程共用同一份数据
与上面的情形相同,但必须是只读的数据(其实代码就是只读数据)。
3. 进程内部通过不同权限映射同一段物理内存
这种情形很容易被忽略。物理内存按页面大小分配,如果数据量不是页面大小的整数倍,那么剩余零头对物理内存就形成了浪费。如果两种访问属性的数据(比如代码有读、执行情形,只读数据只有读权限),他们的大小相当于页面大小都有一部分零头,那么可以把这两个零头拼在一起放在一个物理页面上,然后通过两个虚拟页面映射,就能够到达节约内存的同时,仍能进行权限控制力的目的。
综上,程序实际占用物理内存的计算方法应该是:
程序占用内存 = 映射物理内存的加权和
加权值 = 本程序对该物理内存的映射次数 / 该物理内存在整个系统中被映射的次数
简单的讲,要从虚拟内存量中第1、3种映射情形多算的占用量。第1种情形要全部减去,第3种情形要整个系统做一下平均,取平均值。
-
顶
- 0
-
踩
- 0