内存管理:
背景知识:
- 虚拟地址空间:
每个进程都有自己的虚拟地址空间,对于32位的进程来说空间大小为4GB,由于虚拟地址空间的存在,一个进程中的线程是看不到其他线程的内存的。虚拟地址空间主要包含了四部分内容,其中主要的是用户模式分区和内核模式分区。用户模式分区:自己写的程序,数据,加载的动态库等都加载到这个地方。内核模式分区:线程调度,内存管理等,该部分是不可以被访问的。2. 物理存储器:
虚拟地址空间并不是物理存储器,这个地址空间只不过是内存地址空间,为了能够正常的读/写数据,还需要将物理存储器分配或者映射到相应的地址空间上。当然虚拟地址空间要大的多,这个地址空间中的大部分是闲置的或者尚未分配的。为了使用这部分地址空间可以如下操作。1.预定:VirualAlloc函数(起始地址需要是分配粒度的整数倍,分配粒度一般为64Kb,分配大小需要是系统页面的整数倍,一般是4Kb)2.分配:VirtualAlloc函数(并不需要给整个区域都调拨物理存储器,比如我们预定64Kb大小的区域,分配物理存储器可能只分配8Kb,而且分配的物理存储器是在磁盘上的页交换文件,就是虚拟内存?)3.撤销:VirtualFree函数物理存储器:内存和页交换文件(虚拟内存),当然要读/写页交换文件的时候需要将他载入到内存中才能操作。当我们运行一个.exe或dll文件的时候,系统会预定一块地址空间,并注明与该区域相关联的物理存储器就是.exe文件本身,系统就不用从页交换文件中分配空间了。这种文件称为内存映射文件Window提供了三种机制来对内存进行操控:1.虚拟内存:适合管理大型对象数组或大型结构数组2.内存映射文件:适合管理大型数据流,以及多进程间的共享数据3.堆:大量的小型对象
写时复制:
Windows支持一种机制,允许两个或两个以上的进程共享同一块存储器。不过操作系统会给共享的存储页指定写时复制属性,当有个进程想修改一个共享页面时,操作系统会从内存中找到一个闲置页面并将修改的页面内容复制到这个闲置页面上,再将虚拟地址空间和这个新的页面映射上。区域的类型:1.闲置:区域的虚拟地址没有任何后备存储器2.私有:区域的虚拟地址以系统的页交换文件作为后备存储器3.映像:一开始以映像文件作为后备存储器,但此后不一定以映像文件作为后备存储器。4.已映射:一开始以内存映射文件作为后备存储器,但此后不一定以内存映射文件作为后备存储器。
内存映射文件:
内存映射文件允许开发人员预定一块地址空间区域并给区域调拨物理存储器。该物理存储器来自磁盘上已有的文件,而不是来自系统的页交换文件。优点:1.系统通过内存映射文件加载并运行.exe和动态库文件时,大量节省了页交换文件的空间以及应用程序的启动时间。2.可以通过映射文件来访问磁盘上的数据文件,可以避免直接对文件进行i/o操作和对文件内容进行缓存。3.可以在不同进程间共享数据。
当该程序运行的时候也需要将某页代码载入到内存中。
虚拟内存:
虚拟内存是计算机管理内存的一种技术,它能让应用程序认为它有连续可能的内存。而实际上它被分成多个零碎的屋里碎片。还有部分存储在外部磁盘存储器上。使用虚拟空间也是使用VirualAlloc和VirtualFree这两个函数,当然也要遵守分配粒度和分配大小的规定。虚拟内存与虚拟地址空间:说到这肯定会问虚拟内存和虚拟地址空间是什么关系。我觉得虚拟内存是对于计算机上所有的程序而言,他们能用的内存最多就是虚拟内存那么多。而虚拟地址空间是相对于每个进程而言,对于每个进程他们觉得他们可以访问虚拟地址空间那么大的空间,当进程执行一段代码,这个代码所在的地址空间的地址就是在虚拟内存上,虚拟内存当然是跟物理存储器有映射关系。如果这段代码在实际内存中则运行,不在的话需要进行切换。
线程栈:
系统在会在用户进程的地址空间中预定一块区域,并分配物理存储器,这个区域的大小是可以指定的,在构建应用程序的时候,连接器会把想要的栈的大小写入到.exe或.dll的PE头中。
堆
*结构体的内存(遵循以下几个原则):
- 结构体变量的首地址能够被其最宽基本类型成员的大小所整除
- 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节
- 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节