以前写过一篇理解程序内存, 当时主要是针对用户态,下面再稍微深入一点:

我们以32位程序为例(不启用AWE), 总共4G虚拟空间,其中低2G属于用户态, 高2G属于操作系统内核, 每个程序都有自己的低2G用户空间, 高2G内核空间是所有程序共享的。高2G内核空间中, 属于同一Session的程序又共享相同的session空间:

x86系统所有的内存以64K边界粒度, 4K页面大小分配。

用户态的内存空间,按用途分可以分为: image, mapped file, heap, stack, free等
按状态分可以分为:Free, reserved, commit;
Commit的内存,在被访问时又可能以不同的状态存在, 可能是已经提交到物理内存(RAM),也可能是已页文件的形式存在后台, 如果是页文件形式,访问时会触发换页操作。

我们平时以任务管理器或者Process Explorer, 经常会看到一些不同内存术语:
virtual size: reserve和commit的虚拟内存
Private bytes: 已经commit的私有虚拟内存
working set: commit的虚拟内存中已经被加载到物理内存中的部分
WS private / 内存(专用工作集): 不能和其他程序共享的working set

这些内存的大小关系怎么样?
virtual size 肯定是最大的; WS private肯定是最小的;working set和private bytes大小不好定, 因为working set虽然是表示物理内存, 但它包含共享和非共享两部分, 而private bytes虽然是虚拟内存,却只包含私有部分。
另外我们平时看程序的内存泄漏,主要可以看private bytes 和 WS private.

我们程序里使用的虚拟地址, 它在访问时是如何别转成真正的物理地址的?

1. 我们的虚拟地址被分为页目录索引,页表索引,字节偏移三部分
2. 根据CR3寄存器得到当前进程的页目录表地址, 根据页目录索引得到页目录表项目(PDE), 然后就可以得到该页表的地址
3. 根据页表索引,得到页表项目(PTE)的地址, 然后即可定位到该页面, 根据偏移字节即可访问真正的物理内存

操作系统采用按需换页的算法来实现内存的访问, 也就是说系统会在真正访问一个地址的时候才会把该地址转成有效的物理地址, 如果访问失败, 会触发换页异常, 再真正加载该页面换到物理内存。系统用虚拟地址描述符(VAD, virtual address descriptor)组成的平衡二叉树来跟踪所有的虚拟内存,以确定所有虚拟内存的状态(free, reserver, commit)和属性。

下面说下应用层对程序内存的访问, 按照内存的用途就可以大概划分:
Image: 主要是指二进制模块在内存中存在方式, 比如Exe和Dll, 对应的API比如LoadLibrary。
Mapped file: 主要是指内存映射文件, 可以用来快速的加载大文件 ,或者跨进程共享内存, 对应的API比如 CreateFileMapping.
Stack: 每个线程都有自己的堆栈, 包括用户态堆栈和内核堆栈,虽然堆栈内存分配有大小限制, 但是非常高效,函数的局部变量都存在里面,程序的运行过程(函数的调用过程)实际上是不停的压栈和出栈的过程,大小一般默认保留1M(参见线程堆栈是如何增长的)
Heap: 系统有自己的堆管理器, 虽然效率堆内存分配效率低, 但是没有大小限制, 对应的API比如new, malloc, HeapAlloc

操作系统为我们访问内存提供了各种渠道,我们可以根据需要自己选择, 由下往上可以分为:
虚拟内存: 对应的API如VirtualAlloc(Ex), VirtualFree(Ex), VirtualLock, VirtualProtect, 通过这些API,我们可以直接分配(reserver, commit)大块内存( 4K页面大小), 同时定义修改页面属性, 这是最高效的大内存分配方式。
Win32 堆内存: 对应的API如HeapCreate, HeapAlloc, 堆内存建立在虚拟内存之上,很多时候我们不需要虚拟内存的大块内存,只需要小块内存,操作系统通过堆管理器帮我们解决了这个问题。每个进程启动时系统都会创建一个默认堆,同时我们也可以创建自己的私有堆, 不同模块之间是否共享同一个CRT堆取决于模块的编译选项,(参见基于WinDbg的内存泄漏分析)
CRT 堆内存:C/C++代码中我们最常用的内存分配方式是malloc和new, 通常情况下malloc只负责内存分配, 而new在调用malloc分配内存的同时还有在分配的内存上构造对象的功能。至于malloc的实现方式, 不同的编译器厂商会有不同的实现, 有些可能是通过Win32堆实现,也可能是通过虚拟内存API直接实现。

思考为什么有了虚拟内存API和Win32堆API,还要有CRT堆API?
软件工程里一条比较经典的话是: 任何问题都可以加一个间接层加以解决。操作系统提供的API都是平台相关的, 通过CRT这个间接层实现了平台无关, 同时我们可以在这个间接层上做很多事情, 比如内存泄漏跟踪, 实现自己的内存池等。

如果我们直接调用虚拟内存API分配内存, 这种内存属于那种类型?
实际上按照VMMap的说法, 内存类型还有更多: Image, Mapped File, Shareable, Heap, Managed Heap, Stack, Private Data, Page Table, Unusable, Free.
直接通过VirtualAlloc分配的内存不属于Heap, 应该属于Private Data.

Windows内存小结的更多相关文章

  1. Windows内存小结(有好多图,比较清楚)

    以前写过一篇理解程序内存, 当时主要是针对用户态,下面再稍微深入一点: 我们以32位程序为例(不启用AWE), 总共4G虚拟空间,其中低2G属于用户态, 高2G属于操作系统内核, 每个程序都有自己的低 ...

  2. 第13章 Windows内存体系结构

    13.1 Windows的虚拟地址空间安排 13.1.1虚拟地址空间的分区(即虚拟地址空间布局) 进程的地址空间划分 分区 x86 32位 Windows 3GB用户模式下的x86 32位Window ...

  3. Windows内存管理和linux内存管理

    windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...

  4. windows内存映射学习及帮助类实现

    本文通过创建文件内存映射类,学习windows内存映射相关知识:创建内存映射文件后,可以按照内存操作方式操作文件:支持32位程序处理超过4G大小的文件. 感谢http://blog.csdn.net/ ...

  5. Windows内存管理[转]

    本文主要内容:1.基本概念:物理内存.虚拟内存:物理地址.虚拟地址.逻辑地址:页目录,页表2.Windows内存管理3.CPU段式内存管理4.CPU页式内存管理 一.基本概念1. 两个内存概念物理内存 ...

  6. Windows内存原理与内存管理

    WIndows为每个进程分配了4GB的虚拟地址空间,让每个进程都认为自己拥有4GB的内存空间,4GB怎么来的? 32位 CPU可以取地址的空间为2的32次方,就是4GB(正如16位CPU有20根寻址线 ...

  7. windows内存管理方式以及优缺点

    Windows内存管理方式:页式管理,段式管理,段页式管理 页式管理 将各进程的虚拟空间(逻辑地址)划分为若干个长度相等的页,业内管理把内存空间(物理内存)按照页的大小划分为片或者页面,从而实现了离散 ...

  8. 解决Windows内存问题的两个小工具RamMap和VMMap(这个更牛更好)

    来源:http://www.cr173.com/html/13006_1.html .net程序内存监测分配工具(CLR Profiler for .NET Framework 4)官方安装版 类型: ...

  9. 解决Windows内存问题的两个小工具RamMap和VMMap

    解决Windows内存问题需要对操作系统的深入理解,同时对于如何运用Windows调试器或性能监控器要有工作认知.如果你正试着得到细节,诸如内核堆栈大小或硬盘内存消耗,你会需要调试器命令和内核数据架构 ...

随机推荐

  1. QlikView 权限设置问题和注意

    企业级报表通常都涉及到复杂的权限问题, 比如文本级权限和行级权限,某区域经理只能看到该区域的销售数据.QlikView自然也提供了该种功能. 具体方法: 在Edit script中新建一个tab, 输 ...

  2. 第三章 Docker 入门

    第三章 docker 入门 3.1 确保docker已经就绪 首先查看docker程序是否存在,功能是否正常 [#3#cloudsoar@cloudsoar-virtual-machine ~]$su ...

  3. 解决chrome和IE样式兼容问题

    在页面title中加入: <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"> ...

  4. javascript总结

    javascript:它是一种script脚本语言           脚本语言:就是可以和HTML混合在一起使用的语言,可以用来在IE的客                    户端进行程序编制,从 ...

  5. mysql注入读写文件

    mysql <5.0 读文件:load_file() sql-shell select load_file(''); d:/www/xx/index.php /home/webroot/.... ...

  6. String.Format格式说明——复制于DotNet笔记

    String.Format格式说明   C#格式化数值结果表 字符 说明 示例 输出 C 货币 string.Format("{0:C3}", 2) $2.000 D 十进制 st ...

  7. JBoss CLI

    转自http://www.cnblogs.com/inteliot/archive/2012/08/05/2623719.html   为 了便于维护和管理, JBoss AS 7 提供了命令行接口( ...

  8. 一个用shell写的统计目录下统计文件行数的代码

    此代码是用来回忆当初学的shell脚本,很多知识忘了,写这个花了我三个小时的时间 #!/bin/ksh #count *.c file the num of lineg_csum=0g_hsum=0 ...

  9. selenium总结篇,常见方法和页面元素的操作【转】

    原文:http://www.cnblogs.com/tobecrazy/p/4570494.html 今天,总结一下selenium怎么操作web页面常见的元素. 主要有: 上传 alter dial ...

  10. [java基础]java中static关键字

    1.static概念 static是静态修饰符,什么叫静态修饰符呢?大家都知道,在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态就是指在编译后所分配的内存会一直存在,直到程序 ...