1.内存分布概述

每个进程都有自己的虚拟地址空间,对于32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000到0xFFFFFFFF之间的任意一值。对于64位进程来说,由于64位指针可以表示从0x00000000’00000000到0xFFFFFFFF’FFFFFFFF之间的任一值,因此这个地址空间大小为16EB。因为每个进程都有自己专用的地址空间,当进程中的各线程运行时,它们只能访问属于该进程的内存。线程既看不到属于其他进程的内存,也无法访问它们。

2.虚拟地址空间分区

应用程序虽然有这么大的地址空间可用,但是这只是虚拟地址空间,不是物理存储器。这个地址空间只不过是一个内存的地址区间。

每个进虚拟地址空间被划分为许多分区(partion)。由于地址空间的分区依赖于操作系统的底层实现。因此会随着Windows内核的不同而略有变化。

分区

x86 32

Windows

3G用户模式下的

x86 32位Windows

x64 64位Windows

IA-64 64位的 Windows

空指针

赋值分区

0x00000000

0xFFFFFFFF

0x00000000

0xFFFFFFFF

0x00000000’00000000

0x00000000’0000FFFF

0x00000000’00000000

0x00000000’0000FFFF

用户模式

分区

0x00010000

0x7FFEFFFF

0x00010000

0xBFFEFFFF

0x00000000’00010000

0x000007FF’FFFFFFFF

0x00000000’00010000

0x000006FB’FFFEFFFF

64K禁入

分区

0x7FFF0000

0x7FFFFFFF

0xBFFF0000

0xBFFFFFFF

0x000007FF’FFFF0000

0x000007FF’FFFFFFFF

0x000006FB’FFFF0000

0x000006FB’FFFFFFFF

内核模式

分区

0x80000000

0xFFFFFFFF

0xC0000000

0xFFFFFFFF

0x00000800’00000000

0xFFFFFFFF’FFFFFFFF

0x000006FC’00000000

0xFFFFFFFF’FFFFFFFF

从上表中可能看到,32位Winows内核和64位Windows内核的分区基本一致,唯一的不同在于分区的大小和分区的位置。

2.1.空指针赋值分区

这一分区是进程地址空间中从0x00000000到0x0000FFFF的闭区间,保留该分区的目的是为了帮助程序捕获对于空指针的赋值。没有任何办法可以让我们分配到位于这一地址空间的虚拟内存,即使是使用Win32的应用程序编程接口(appliction programming interface,通常简称API)也不例外。如果进程中的线程试图读取或写入于这一分区内的内存地址,就会引发访问违规。

如果malloc无法分配足够的内在,那么它会返回NULL。地址空间中的这一分区是禁止访问的,所以会引发内存访问违规并导致进程被终止。这一特性可以帮助开发人员发现应用程序中的缺陷。

2.2.用户模式分区

这一分区是进程地址空间的驻地,可用的地址空间和用户模式分区的大小取决于CPU体系结构。进程无法通过指针来读取、写入或以任何方式,访问驻留在这一分区中的其他进程的数据。对所有应用程序来说,进程的大部分数据都保存在这一分区。由于每个进程都有自己的数据分区。因此一个应用程序破另一个应用程序的可能性就非常小,从而使得整个系统更加坚固。

在早期版本的Windows中,Microsoft不允许用户程序访问2GB以上的地址空间,为了让此类应用程序即使在用模式分区大于2GB的环境下仍能正常运行,Microsoft提供代了一种模式来增大用户模式分区,最多不超过3GB。

当前系统即将运行一个应用程序时,它会检查应用程序在链接时是否使用了/LARGEADDRESSAWARE链接器开关。如果是,则相应于应用程序在声明它会充分利用大用户模式地址空间,而不会对内在地址进行任何不当的操作。反之,如果应用程序在链接时没有使用/LARGEADDRESSAWARE开关,那么操作系统会保留用户模式分区中2GB以上到内核模式开始处的整个部分。

2.2.1.程序内存使用分布

可参考https://en.wikipedia.org/wiki/Data_segment#Program_memory

2.2.1.1.Text

【原文】

The code segment, also known as a text segment or simply as text, is where a portion of an object file or the corresponding section of the program's virtual address space that contains executable instructions is stored and is generally read-only and fixed size.

代码段,通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。

在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

代码段,也称为文本段,或简称为文本,是指存储有可执行指令的对象文件或程序的虚拟地址空间的相应部分的一部分,并且通常是只读的和固定大小的。

2.2.1.2.Data

【原文】

The .data segment contains any global or static variables which have a pre-defined value and can be modified. That is any variables that are not defined within a function (and thus can be accessed from anywhere) or are defined in a function but are defined as static so they retain their address across subsequent calls. Examples, in C, include:

int val = 3;

char string[] = "Hello World";

The values for these variables are initially stored within the read-only memory (typically within .text) and are copied into the .data segment during the start-up routine of the program.

数据段包含任何具有预定义值并可被修改的全局变量或静态变量。这是任何函数中未定义的变量(因此可以从任何地方访问)或在函数中定义,但被定义为静态的,因此它们在随后的调用中保留它们的地址。C中的例子包括:

int val = 3;

char string[] = "Hello World";

这些变量的值最初存储在只读存储器(通常在.text中),并在程序的启动例程期间复制到.DATA段。

2.2.1.3.BSS

【原文】

The BSS segment, also known as uninitialized data, is usually adjacent to the data segment. The BSS segment contains all global variables and static variables that are initialized to zero or do not have explicit initialization in source code. For instance, a variable defined as static int i; would be contained in the BSS segment.

BBS段,也称为未初始化数据段,通常与数据段相邻,包含初始化为或在源码中未进行显式初始化的全局变量和静态变量。例如,定义为静态整型变量i将被包含在BSS段中。

2.2.1.4.Heap

【原文】

The heap area commonly begins at the end of the .bss and .data segments and grows to larger addresses from there. The heap area is managed by malloc, calloc, realloc, and free, which may use the brk and sbrk system calls to adjust its size (note that the use of brk/sbrk and a single "heap area" is not required to fulfill the contract of malloc/calloc/realloc/free; they may also be implemented using mmap/munmap to reserve/unreserve potentially non-contiguous regions of virtual memory into the process' virtual address space). The heap area is shared by all threads, shared libraries, and dynamically loaded modules in a process.

堆区域通常在.BSS和.DATA段的末尾开始,并从那里扩展到更大的地址。堆区域由Maloc、CaloC、ReLoLc和空闲管理,可以使用BRK和SBRK系统调用来调整其大小(注意使用BRK/SBRK和单个堆区)不需要履行MalC/CaloC/ReAlOLC/FILE的合同;也可以使用MMAP/MunMMAP来实现。将虚拟内存的潜在非相邻区域保留/不保留到进程的“虚拟地址空间”中。堆区域由进程中的所有线程、共享库和动态加载模块共享。

2.2.1.5.Stack

【原文】

Main article: Call stack

The stack area contains the program stack, a LIFO structure, typically located in the higher parts of memory. A "stack pointer" register tracks the top of the stack; it is adjusted each time a value is "pushed" onto the stack. The set of values pushed for one function call is termed a "stack frame". A stack frame consists at minimum of a return address. Automatic variables are also allocated on the stack.

The stack area traditionally adjoined the heap area and they grew towards each other; when the stack pointer met the heap pointer, free memory was exhausted. With large address spaces and virtual memory techniques they tend to be placed more freely, but they still typically grow in a converging direction. On the standard PC x86 architecture the stack grows toward address zero, meaning that more recent items, deeper in the call chain, are at numerically lower addresses and closer to the heap. On some other architectures it grows the opposite direction.

主要文章:调用堆栈

堆栈区域包含程序栈,即LIFO结构,通常位于内存的较高部分。一个“堆栈指针”寄存器跟踪堆栈的顶部;每当一个值被“推”到堆栈时,它就被调整。一个函数调用所推的值集称为“堆栈框架”。堆栈帧包括返回地址的最小值。自动变量也被分配到堆栈上。

堆栈区域传统上毗邻堆区域,它们彼此生长;当堆栈指针遇到堆指针时,空闲内存被耗尽。随着大的地址空间和虚拟内存技术,它们倾向于更自由地放置,但它们仍然通常在收敛的方向上生长。在标准的PC x86架构上,堆栈向地址零增长,这意味着在调用链中较近的项在数值较低的地址上更接近堆。在其他一些架构上,它生长了相反的方向。

2.3.内核模式分区

这一分区是操作系统代码的驻地,与线程调度、内存管理、文件系统支持、网络支持以及设备驱动程序相关的代码都载入到该分区。在这一分区内的任何东西为所有进程共有。虽然这一分区就在每个进程中用户模式分区的上方,但该分区中的所有代码和数据都被完全保护起来。如果一个应用程序试图读取或写入这一分区中的内存地址,会引发违规。在默认情况下,访问违规会导致系统先向用户显示一个消息框,然后结束应用程序。

3.堆栈段说明
3.1.栈(Stack)
       当系统创建线程时,会为线程栈预订一块空间区域(每个线程都有自己的栈),并给区域调拨一些物理存储器。默认情况下,系统会预订1MB的地址空间并调拨两个页面的存储器。但是,在构建应用程序时开发人员可以通过两种方法来改变该默认值,一种方法是使用Microsoft C++编译器的/F选项,另一种方法是使用Microsoft C++链接器的/STACK选项。在构建应用程序时,链接器会把想要的栈的大小写入到.exe或.dll文件的PE文件头中。当前系统创线栈的时候,会根据PE文件头中的大小来预订地址空间的区域。但是在调用CreateThread或_beginthreadex函数时,开发人员也可以另外指定需要在一开始就调拨的存储器数量。这两个函数都有一个参数,可以用来指定一开始要调拨给线程栈的地址空间区域的存储器的大小。如果该参数设为0,那么系统会使用PE文件头中指定的大小,所使用的都是默认值(即区域大小为1MB),每次调拨一个存储页面。
3.2.堆(Heap)
       堆非常适用分配大量的小型数据。堆是用来管理链表和树的最佳方式。堆的优点是它能让我们专心解决手头上的问题,而不必理会分配粒度和页面边界这类事情。堆的缺点是分配和释放内存块的速度比其他方式慢,而且也无法对物理存储器的调拨和摊销进行直接控制。
       进程初始化时候,系统会在进程的地址空间中创建一个堆,这个堆被称为进程的默认堆(Default heep)。在默认情况下,这个堆的地址空间区域的大小是1MB。但是系统可以增大进程的默认堆,使它大于1MB。我们也可以在创建应用程序的时候用/HEAP链接器开关来改变默认区域大小。由于动态链接库(.DLL)没有与之关联的堆,因此在创建DLL的时候不应使用/HEAP开关。

Windows 内存机制说明相关推荐

  1. dll占的究竟是谁的空间?——浅谈Windows内存机制

    近来工作比较空闲,所以就上csdn.net看看帖子什么的,两个多月前,我在VC/MFC板块中发了这么一个帖子:dll占的究竟是谁的空间?详细参考: http://topic.csdn.net/u/20 ...

  2. 全面介绍Windows内存管理机制及C++内存分配实例(六):堆栈

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  3. 全面介绍Windows内存管理机制及C++内存分配实例(五):堆

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  4. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  5. 全面介绍Windows内存管理机制及C++内存分配实例(三):虚拟内存

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  6. 全面介绍Windows内存管理机制及C++内存分配实例(二):内存状态查询

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  7. 全面介绍Windows内存管理机制及C++内存分配实例(一):进程空间

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

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

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

  9. windows内存管理和API函数

    windows内存管理知识: 1.分段或分页内存管理 2.物理地址和虚拟地址,虚拟地址空间. 3.虚拟内存布局,内存分工,堆,栈. 4.内存存取权限. 5.标准C内存管理函数与windows内存管理A ...

  10. Windows消息机制学习笔记(一)—— 消息队列

    Windows消息机制学习笔记(一)-- 消息队列 基本概念 实验一:使用代码画出最简单窗口 第一步:编译并运行以下代码 第二步:查看运行结果 第三步:使用其它窗口对其进行覆盖,观察效果 总结 消息队 ...

最新文章

  1. 第2关:利用栈判断字符串括号是否匹配
  2. SpringBoot(五)ResponseBodyAdvice
  3. Python的常用模块
  4. 8.22 13.1-13.3
  5. java中的exception stack有时候不输出的原因
  6. Linux 监控数据库资源占用
  7. 新文速递 | 盒装数据:一种基于数据盒的数据产品形态
  8. (转)Arcgis for JS实现台风运动路径与影像范围的显示
  9. 4KB/4MB 32位分页模式下的线性地址翻译以及CR3
  10. SpringSecurity系列(三) Spring Security 表单登录
  11. [GCN] 图卷积知识梳理 -持续更新
  12. 【行为识别】基于matlab轨迹法行为识别【含Matlab源码 375期】
  13. linux上运行.exe程序方法
  14. 怎么在Eclipse中安装WindowsBuilder
  15. Ubuntu 用vsftpd 配置FTP服务器教程详解
  16. 联想e550笔记本怎么样_摄像头是亮点 — Lenovo 联想 ThinkPad E550C 笔记本 简单评测...
  17. springboot集成Swagger3.0
  18. adb 重命名_adb 常用命令-转载
  19. 小白求答疑,在vs连接数据库的一段配置代码有问题
  20. Spring Boot+Vue项目打包部署

热门文章

  1. 关于 国产麒麟系统使用killall命令杀死模糊匹配进程失败“未找到该进程” 的解决方法
  2. Microsoft store 无法联网,显示Microsoft Store需要联网,你似乎没有联网
  3. 12.12 生日快乐
  4. 设计模式——原形模式
  5. itunes备份文件的位置在哪
  6. WinNTSetup安装Windows10 wim格式文件的使用教程
  7. 华为al00的计算机在哪,(详细)华为畅享8 LDN-AL00的USB调试模式在哪里开启的流程...
  8. 第一章-第六题(帮人抢票,帮人选课这些软件是否合法 你怎么看?)--By梁旭晖...
  9. java 过滤script_Java过滤任意(script,html,style)标签符,返回纯文本--封装类
  10. rust编程 UI框架 -druid -Selector选择器