当前位置:文档之家› 全面介绍Windows内存管理机制及C++内存分配实例

全面介绍Windows内存管理机制及C++内存分配实例

全面介绍Windows内存管理机制及C++内存分配实例
全面介绍Windows内存管理机制及C++内存分配实例

本文背景:

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

本文目的:

对Windows内存管理机制了解清楚,有效的利用C++内存函数管理和使用内存。

本文内容:

本文一共有六节,由于篇幅较多,故按节发表。其他章节请看本人博客的Windows内存管理及C++内存分配实例(二)(三)(四)(五)和(六)。

1. 进程地址空间

1.1地址空间

·32|64位的系统|CPU

操作系统运行在硬件CPU上,32位操作系统运行于32位CPU上,64位操作系统运行于64位CPU上;目前没有真正的64位CPU。

32位CPU一次只能操作32位二进制数;位数多CPU设计越复杂,软件设计越简单。

软件的进程运行于32位系统上,其寻址位也是32位,能表示的空间是232=4G,范围从0x0000 0000~0xFFFF FFFF。

·NULL指针分区

范围:0x0000 0000~0x0000 FFFF

作用:保护内存非法访问

例子:分配内存时,如果由于某种原因分配不成功,则返回空指针0x0000 0000;当用户继续使用比如改写数据时,系统将因为发生访问违规而退出。

那么,为什么需要那么大的区域呢,一个地址值不就行了吗?我在想,是不是因为不让8或16位的程序运行于32位的系统上呢?!因为NULL分区刚好范围是16的进程空间。

·独享用户分区

范围:0x0001 0000~0x7FFE FFFF

作用:进程只能读取或访问这个范围的虚拟地址;超越这个范围的行为都会产生违规退出。例子:

程序的二进制代码中所用的地址大部分将在这个范围,所有exe和dll文件都加载到这个。每个进程将近2G的空间是独享的。

注意:如果在boot.ini上设置了/3G,这个区域的范围从2G扩大为3G:0x0001 0000~0xBFFE FFFF。

·共享内核分区

范围:0x8000 0000~0xFFFF FFFF

作用:这个空间是供操作系统内核代码、设备驱动程序、设备I/O高速缓存、非页面内存池的分配、进程目表和页表等。

例子:

这段地址各进程是可以共享的。

注意:如果在boot.ini上设置了/3G,这个区域的范围从2G缩小为1G:0xC000 0000~0xFFFF FFFF。

通过以上分析,可以知道,如果系统有n个进程,它所需的虚拟空间是:2G*n+2G (内核只需2G的共享空间)。

1.2地址映射

·区域

区域指的是上述地址空间中的一片连续地址。区域的大小必须是粒度(64k) 的整数倍,不是的话系统自动处理成整数倍。不同CPU粒度大小是不一样的,大部分都是64K。

区域的状态有:空闲、私有、映射、映像。

在你的应用程序中,申请空间的过程称作保留(预订),可以用VirtualAlloc;删除空间的过程为释放,可以用VirtualFree。

在程序里预订了地址空间以后,你还不可以存取数据,因为你还没有付钱,没有真实的RAM和它关联。

这时候的区域状态是私有;

默认情况下,区域状态是空闲;

当exe或DLL文件被映射进了进程空间后,区域状态变成映像;

当一般数据文件被映射进了进程空间后,区域状态变成映射。

·物理存储器

Windows各系列支持的内存上限是不一样的,从2G到64G不等。理论上32位CPU,硬件上只能支持4G内存的寻址;能支持超过4G的内存只能靠其他技术来弥补。顺便提一下,Windows个人版只能支持最大2G内存,Intel使用Address Windows Extension (AWE) 技术使得寻址范围为236=64G。当然,也得操作系统配合。

内存分配的最小单位是4K或8K,一般来说,根据CPU不同而不同,后面你可以看到可以通过系统函数得到区域粒度和页面粒度。

·页文件

页文件是存在硬盘上的系统文件,它的大小可以在系统属性里面设置,它相当于物理内存,所以称为虚拟内存。事实上,它的大小是影响系统快慢的关键所在,如果物理内存不多的情况下。

每页的大小和上述所说内存分配的最小单位是一样的,通常是4K或8K。

·访问属性

物理页面的访问属性指的是对页面进行的具体操作:可读、可写、可执行。CPU一般不支持可执行,它认为可读就是可执行。但是,操作系统提供这个可执行的权限。

PAGE_NOACCESS

PAGE_READONL Y

PAGE_READWRITE

PAGE_EXECUTE

PAGE_EXECUTE_READ

PAGE_EXECUTE_READWRITE

这6个属性很好理解,第一个是拒绝所有操作,最后一个是接受收有操作;

PAGE_WRITECOPY

PAGE_EXECUTE_WRITECOPY

这两个属性在运行同一个程序的多个实例时非常有用;它使得程序可以共享代码段和数据段。一般情况下,多个进程只读或执行页面,如果要写的话,将会Copy页面到新的页面。通过映射exe文件时设置这两个属性可以达到这个目的。

PAGE_NOCACHE

PAGE_WRITECOMBINE

这两个是开发设备驱动的时候需要的。

PAGE_GUARD

当往页面写入一个字节时,应用程序会收到堆栈溢出通知,在线程堆栈时有用。

·映射过程

进程地址空间的地址是虚拟地址,也就是说,当取到指令时,需要把虚拟地址转化为物理地址才能够存取数据。这个工作通过页目和页表进行。

从图中可以看出,页目大小为4K,其中每一项(32位)保存一个页表的物理地址;每个页表大小为4K,其中每一项(32位)保存一个物理页的物理地址,一共有1024个页表。利用这4K+4K*1K=4.4M的空间可以表示进程的1024*1024* (一页4K) =4G的地址空间。

进程空间中的32位地址如下:

高10位用来找到1024个页目项中的一项,取出页表的物理地址后,利用中10位来得到页表项的值,根据这个值得到物理页的地址,由于一页有4K大小,利用低12位得到单元地址,这样就可以访问这个内存单元了。

每个进程都有自己的一个页目和页表,那么,刚开始进程是怎么找到页目所在的物理页呢?答案是CPU的CR3寄存器会保存当前进程的页目物理地址。

当进程被创建时,同时需要创建页目和页表,一共需要4.4M。在进程的空间中,0xC030 0000~0xC030 0FFF是用来保存页目的4k空间。0xC000 0000~0xC03F FFFF是用来保存页表的4M空间。也就是说程序里面访问这些地址你是可以读取页目和页表的具体值的(要工作在内核方式下)。有一点我不明白的是,页表的空间包含了页目的空间!

至于说,页目和页表是保存在物理内存还是页文件中,我觉得,页目比较常用,应该在物理内存的概率大点,页表需要时再从页文件导入物理内存中。

页目项和页表项是一个32位的值,当页目项第0位为1时,表明页表已经在物理内存中;当页表项第0位为1时,表明访问的数据已经在内存中。还有很多数据是否已经被改变,是否可读写等标志。另外,当页目项第7位为1时,表明这是一个4M的页面,这值已经是物理页地址,用虚拟地址的低22位作为偏移量。还有很多:数据是否已经被改变、是否可读写等标志。

1.3 一个例子

·编写生成软件程序exe

软件描述如下:

Main ()

{

1:定义全局变量

2:处理函数逻辑(Load 所需DLL库,调用方法处理逻辑)

3:定义并实现各种方法(方法含有局部变量)

4:程序结束

}

将程序编译,生成exe文件,附带所需的DLL库。

·exe文件格式

exe文件有自己的格式,有若干节(section):.text用来放二进制代码(exe或dll);.data用来放各种全局数据。

.text

指令1:move a, b

指令2:add a, b

.data

数据1:a=2

数据2:b=1

这些地址都是虚拟地址,也就是进程的地址空间。

·运行exe程序

建立进程:运行这个exe程序时,系统会创建一个进程,建立进程控制块PCB,生成进程页目和页表,放到PCB中。

数据对齐:数据的内存地址除以数据的大小,余数为0时说明数据是对齐的。现在的编译器编译时就考虑数据对齐的问题,生成exe文件后,数据基本上是对齐的,CPU运行时,寄存器有标志标识CPU是否能够自动对齐数据,如果遇到不能对齐的情况,或者通过两次访问内存,或者通知操作系统处理。

要注意的是,如果数据没有对齐,CPU处理的效率是很低的。

文件映射:系统不会将整个exe文件和所有的DLL文件装载进物理内存中,同时它也不会装载进页面文件中。相反,它会建立文件映射,也就是利用exe本身当作页面文件。系统将部分二进制代码装载进内存,分配页面给它。

假设分配了一个页面,物理地址为0x0232 FFF1。其中装载的一个指令虚拟地址为0x4000 1001=0100 0000 00 0000 0000 01 0000 0000 0001。一个页面有4K,系统会将指令保存在低12位0x0001的地址处。同时,系统根据高10位0x0100找到页目项,如果没有关联的页表,系统会生成一个页表,分配一个物理页;然后,根据中10位0x0001找到表项,将物理地址0x0232 FFF1存进去。

执行过程:

执行时,当系统拿到一个虚拟地址,就根据页目和页表找到数据的地址,根据页目上的值可以判断页表是在页文件中还是在内存中;

如果在页文件中,会将页面导入内存,更新页目项。读取页表项的值后,可以判断数据页文件中还是在物理内存中;如果在页文件中,会导入到内存中,更新页表项。最终,拿到了数据。

在分配物理页的过程中,系统会根据内存分配的状况适当淘汰暂时不用的页面,如果页面内容改变了(通过页表项的标志位),保存到页文件中,系统会维护内存与页文件的对应关系。

由于将exe文件当作内存映射文件,当需要改变数据,如更改全局变量的值时,利用Copy-On-Write的机制,重新生成页文件,将结果保存在这个页文件中,原来的页文件还是需要被其他进程实例使用的。

在清楚了指令和数据是如何导入内存,如何找到它们的情况下,剩下的就是CPU不断的取指令、运行、保存数据的过程了,当进程结束后,系统会清空之前的各种结构、释放相关的物理内存和删除页文件。

本文背景:

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

本文目的:

对Windows内存管理机制了解清楚,有效的利用C++内存函数管理和使用内存。

本文内容:

本文一共有六节,由于篇幅较多,故按节发表。其他章节请看本人博客的Windows内存管理及C++内存分配实例(一)(三)(四)(五)和(六)。

2. 内存状态查询函数

2.1系统信息

Windows 提供API可以查询系统内存的一些属性,有时候我们需要获取一些页面大小、分配粒度等属性,在分配内存时用的上。

请看以下C++程序:

SYSTEM_INFO sysInfo;

GetSystemInfo(&sysInfo);

cout<<"机器属性:"<

cout<<"页大小="<

cout<<"分配粒度="<

cout<<"用户区最小值="<

cout<<"用户区最大值="

<

结果如下:

可以看出,页面大小是4K,区域分配粒度是64K,进程用户区是0x0001 0000~0x7FFE FFFF。

2.2内存状态

·内存状态可以获取总内存和可用内存,包括页文件和物理内存。

请看以下C++程序:

MEMORYSTATUS memStatus;

GlobalMemoryStatus(&memStatus);

cout<<"内存初始状态:"<

cout<<"内存繁忙程度="<

cout<<"总物理内存="<

cout<<"可用物理内存="<

cout<<"总页文件="<

cout<<"可用页文件="<

cout<<"总进程空间="<

cout<<"可用进程空间="<

结果如下:

可以看出,总物理内存是1G,可用物理内存是510兆,总页文件是2.5G,这个是包含物理内存的页文件;可用页文件是1.9G。这里还标识了总进程空间,还有可用的进程空间,程序只用了22兆的内存空间。这里说的都是大约数。

内存繁忙程序是标识当前系统内存管理的繁忙程序,从0到100,其实用处不大。

·在函数里面静态分配一些内存后,看看究竟发生什么

char stat[65536];

MEMORYSTATUS memStatus1;

GlobalMemoryStatus(&memStatus1);

cout<<"静态分配空间:"<

printf("指针地址=%x\n",stat);

cout<<"减少物理内存="<

cout<<"减少可用页文件="<

cout<<"减少可用进程空间="<

memSta tus1.dwAvailVirtual<

结果如下:

可以看出,物理内存、可用页文件和进程空间都没有损耗。因为局部变量是分配在线程堆栈里面的,每个线程系统都会建立一个默认1M大小的堆栈给线程函数调用使用。如果分配超过1M,就会出现堆栈溢出。

·在函数里面动态分配300M的内存后,看看究竟发生什么

char *dynamic=new char[300*1024*1024];

MEMORYSTATUS memStatus2;

GlobalMemoryStatus(&memStatus2);

cout<<"动态分配空间:"<

printf("指针地址=%x\n",dynamic);

cout<<"减少物理内存="<

cout<<"减少可用页文件="<

cout<<"区域基地址="<

cout<<"区域邻近页面状态="<

cout<<"区域保护属性="<

cout<<"页面基地址="<

printf("arrayA指针地址=%x\n",arrayA);

cout<<"从页面基地址开始的大小="<

cout<<"邻近页面物理存储器类型="<

cout<<"页面保护属性="<

第二次查询:

MEMORY_BASIC_INFORMATION mbiB;

VirtualQuery(arrayB,&mbiB,len);

cout<<"静态内存地址属性:"<

cout<<"区域基地址="<

cout<<"区域邻近页面状态="<

cout<<"区域保护属性="<

cout<<"页面基地址="<

printf("arrayB指针地址=%x\n",arrayB);

cout<<"从页面基地址开始的大小="<

cout<<"邻近页面物理存储器类型="<

cout<<"页面保护属性="<

说明:区域基地址指的是给定地址所在的进程空间区域;

邻近页面状态指的是与给定地址所在页面状态相同页面的属性:MEM_FREE(空闲=65536)、MEM_RESERVE(保留=8192)和MEM_COMMIT(提交=4096)。

区域保护属性指的是区域初次被保留时被赋予的保护属性:PAGE_READONL Y(2)、PAGE_READWRITE(4)、PAGE_WRITECOPY(8)和PAGE_EXECUTE_WRITECOPY(128)等等。

页面基地址指的是给定地址所在页面的基地址。

从页面基地址开始的区域页面的大小,指的是与给定地址所在页面状态、保护属性相同的页面。

邻近页面物理存储器类型指的是与给定地址所在页面相同的存储器类型,包括:MEM_PRIV ATE(页文件=131072)、MEM_MAPPED(文件映射=262144)和MEM_IMAGE (exe映像=16777216)。

页面保护属性指的是页面被指定的保护属性,在区域保护属性指定后更新。

结果如下:

如前所说,这是在堆栈区域0x0004 0000里分配的,后分配的地址arrayB反而更小,符合堆栈的特性。arrayA和arrayB它们处于不同的页面。页面都受页文件支持,并且区域都是提交的,是系统在线程创建时提交的。

·C++动态分配了两次内存,一次是1K大一点,一个是64K左右。所以应该不会在一个区域。

char *dynamicA=new char[1024];

char *dynamicB=new char[65467];

VirtualQuery(dynamicA,&mbiA,len);

cout<<"动态内存地址属性:"<

cout<<"区域基地址="<

cout<<"区域邻近页面状态="<

cout<<"区域保护属性="<

cout<<"页面基地址="<

printf("dynamicA指针地址=%x\n",dynamicA);

cout<<"从页面基地址开始的大小="<

cout<<"邻近页面物理存储器类型="<

cout<<"页面保护属性="<

VirtualQuery(dynamicB,&mbiB,len);

cout<<"动态内存地址属性:"<

cout<<"区域基地址="<

cout<<"区域邻近页面状态="<

cout<<"区域保护属性="<

cout<<"页面基地址="<

printf("dynamicB指针地址=%x\n",dynamicB);

cout<<"从页面基地址开始的大小="<

cout<<"邻近页面物理存储器类型="<

cout<<"页面保护属性="<

结果如下:

这里是动态分配,dynamicA和dynamicB处于两个不同的区域;同样,页面都受页文件支持,并且区域都是提交的。

第二个区域是比64K大的,由分配粒度可知,区域至少是128K。那么,剩下的空间也是提交的吗,如果是的话那就太浪费了。看看就知道了:0x00E2 1000肯定在这个空间里,所以查询如下:

VirtualQuery((char*)0xE23390,&mbiB,len);

cout<<"动态内存地址属性:"<

cout<<"区域基地址="<

cout<<"区域邻近页面状态="<

cout<<"区域保护属性="<

cout<<"页面基地址="<

printf("dynamicB指针地址=%x\n",0xE21000);

cout<<"从页面基地址开始的大小="<

cout<<"邻近页面物理存储器类型="<

cout<<"页面保护属性="<

结果如下:

可以看出,邻近页面状态为保留,还没提交,预料之中;0x00E1 0000 这个区域的大小可以计算出来:69632+978944=1024K。系统动态分配了1M的空间,就为了64K左右大小的空间。可能是为了使得下次有要求分配时时不用再分配了。

本文来自CSDN博客,转载请标明出处:https://www.doczj.com/doc/0817880422.html,/yeming81/archive/2008/01/16/2046207.aspx

本文背景:

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

本文目的:

对Windows内存管理机制了解清楚,有效的利用C++内存函数管理和使用内存。

本文内容:

本文一共有六节,由于篇幅较多,故按节发表。其他章节请看本人博客的Windows内存管理及C++内存分配实例(一)(二)(四)(五)和(六)。

3. 内存管理机制--虚拟内存(VM)

·虚拟内存使用场合

虚拟内存最适合用来管理大型对象或数据结构。比如说,电子表格程序,有很多单元格,但是也许大多数的单元格是没有数据的,用不着分配空间。也许,你会想到用动态链表,但是访问又没有数组快。定义二维数组,就会浪费很多空间。

它的优点是同时具有数组的快速和链表的小空间的优点。

·分配虚拟内存

如果你程序需要大块内存,你可以先保留内存,需要的时候再提交物理存储器。在需要的时候再提交才能有效的利用内存。一般来说,如果需要内存大于1M,用虚拟内存比较好。

·保留

用以下Windows 函数保留内存块

VirtualAlloc (PVOID 开始地址,SIZE_T 大小,DWORD 类型,DWORD 保护属性)

一般情况下,你不需要指定“开始地址”,因为你不知道进程的那段空间是不是已经被占用了;所以你可以用NULL。“大小”是你需要的内存字节;“类型”有MEM_RESERVE(保留)、MEM_RELEASE(释放)和MEM_COMMIT(提交)。“保护属性”在前面章节有详细介绍,只能用前六种属性。

如果你要保留的是长久不会释放的内存区,就保留在较高的空间区域,这样不会产生碎片。用这个类型标志可以达到:

MEM_RESERVE|MEM_TOP_DOWN。

C++程序:保留1G的空间

LPVOID

pV=VirtualAlloc(NULL,1000*1024*1024,MEM_RESERVE|MEM_TOP_DOWN,PAGE_READ WRITE);

if(pV==NULL)

cout<<"没有那么多虚拟空间!"<

MEMORYSTATUS memStatusVirtual1;

GlobalMemoryStatus(&memStatusVirtual1);

cout<<"虚拟内存分配:"<

printf("指针地址=%x\n",pV);

cout<<"减少物理内存="<

cout<<"减少可用页文件="<

cout<<"减少可用进程空间="

<

结果如下:

可见,进程空间减少了1G;减少的物理内存和可用页文件用来管理页目和页表。但是,现在访问空间的话,会出错的:

int * iV=(int*)pV;

//iV[0]=1;现在访问会出错,出现访问违规

·提交

你必须提供一个初始地址和提交的大小。提交的大小系统会变成页面的倍数,因为只能按页面提交。指定类型是MEM_COMMIT。保护属性最好跟区域的保护属性一致,这样可以提高系统管理的效率。

C++程序:提交100M的空间

LPVOID pP=VirtualAlloc(pV,100*1024*1024,MEM_COMMIT,PAGE_READWRITE);

if(pP==NULL)

cout<<"没有那么多物理空间!"<

int * iP=(int*)pP;

iP[0]=3;

iP[100/sizeof(int)*1024*1024-1]=5;//这是能访问的最后一个地址

//iP[100/sizeof(int)*1024*1024]=5;访问出错

·保留&提交

你可以用类型MEM_RESERVE|MEM_COMMIT一次全部提交。但是这样的话,没有有效地利用内存,和使用一般的C++动态分配内存函数一样了。

·更改保护属性

更改已经提交的页面的保护属性,有时候会很有用处,假设你在访问数据后,不想别的函数再访问,或者出于防止指针乱指改变结构的目的,你可以更改数据所处的页面的属性,让别人无法访问。

VirtualProtect (PVOID 基地址,SIZE_T 大小,DWORD 新属性,DWORD 旧属性)

“基地址”是你想改变的页面的地址,注意,不能跨区改变。

C++程序:更改一页的页面属性,改为只读,看看还能不能访问

DWORD protect;

iP[0]=8;

VirtualProtect(pV,4096,PAGE_READONLY,&protect);

int * iP=(int*)pV;

iP[1024]=9;//可以访问,因为在那一页之外

//iP[0]=9;不可以访问,只读

//还原保护属性

VirtualProtect(pV,4096,PAGE_READWRITE,&protect);

cout<<"初始值="<

·清除物理存储器内容

清除页面指的是,将页面清零,也就是说当作页面没有改变。假设数据存在物理内存中,系统没有RAM页面后,会将这个页面暂时写进虚拟内存页文件中,这样来回的倒腾系统会很慢;如果那一页数据已经不需要的话,系统可以直接使用。当程序需要它那一页时,系统会分配另一页给它。

VirtualAlloc (PVOID 开始地址,SIZE_T 大小,DWORD 类型,DWORD 保护属性)

“大小”如果小于一个页面的话,函数会执行失败,因为系统使用四舍五入的方法;“类型”是MEM_RESET。

有人说,为什么需要清除呢,释放不就行了吗?你要知道,释放了后,程序就无法访问了。现在只是因为不需要结构的内容了,顺便提高一下系统的性能;之后程序仍然需要访问这个结构的。

C++程序:

清除1M的页面:

PVOID re=VirtualAlloc(pV,1024*1024,MEM_RESET,PAGE_READWRITE);

if(re==NULL)

cout<<"清除失败!"<

这时候,页面可能还没有被清零,因为如果系统没有RAM请求的话,页面内存保存不变的,为了看看被清零的效果,程序人为的请求大量页面:

C++程序:

VirtualAlloc((char*)pV+100*1024*1024+4096,memStatus.dwAvailPhys+10000000,MEM_COM MIT,PAGE_READWRITE);//没访问之前是不给物理内存的。

char* pp=(char*)pV+100*1024*1024+4096;

for(int i=0;i

pp[i]='V';//逼他使用物理内存,而不使用页文件

GlobalMemoryStatus(&memStatus);

cout<<"内存初始状态:"<

cout<<"长度="<

cout<<"内存繁忙程度="<

cout<<"总物理内存="<

cout<<"可用物理内存="<

cout<<"总页文件="<

cout<<"可用页文件="<

cout<<"总进程空间="<

cout<<"可用进程空间="<

cout<<"清除后="<

结果如下:

当内存所剩无几时,系统将刚清除的内存页面分配出去,同时不会把页面的内存写到虚拟页面文件中。可以看见,原先是8的值现在是0了。

·虚拟内存的关键之处

虚拟内存存在的优点是,需要的时候才真正分配内存。那么程序必须决定何时才提交内存。

如果访问没有提交内存的数据结构,系统会产生访问违规的错误。提交的最好方法是,当你程序需要访问虚拟内存的数据结构时,假设它已经是分配内存的,然后异常处理可能出现的错误。对于访问违规的错误,就提交这个地址的内存。

·释放

可以释放整个保留的空间,或者只释放分配的一些物理内存。

释放特定分配的物理内存:

如果不想释放所有空间,可以只释放某些物理内存。

“开始地址”是页面的基地址,这个地址不一定是第一页的地址,一个窍门是提供一页中的某个地址就行了,因为系统会做页边界处理,取该页的首地址;“大小”是页面的要释放的字节数;“类型”是MEM_DECOMMIT。

C++程序:

//只释放物理内存

VirtualFree((int*)pV+2000,50*1024*1024,MEM_DECOMMIT);

int* a=(int*)pV;

a[10]=2;//可以使用,没有释放这一页

MEMORYSTATUS memStatusVirtual3;

GlobalMemoryStatus(&memStatusVirtual3);

cout<<"物理内存释放:"<

cout<<"增加物理内存="<

cout<<"增加可用页文件="<

cout<<"增加可用进程空间="

<

结果如下:

可以看见,只释放物理内存,没有释放进程的空间。

释放整个保留的空间:

VirtualFree (LPVOID 开始地址,SIZE_T 大小,DWORD 类型)

“开始地址”一定是该区域的基地址;“大小”必须是0,因为只能释放整个保留的空间;“类型”是MEM_RELEASE。

C++程序:

VirtualFree(pV,0,MEM_RELEASE);

//a[10]=2;不能使用了,进程空间也释放了

MEMORYSTATUS memStatusVirtual4;

GlobalMemoryStatus(&memStatusVirtual4);

cout<<"虚拟内存释放:"<

cout<<"增加物理内存="<

cout<<"增加可用页文件="<

cout<<"增加可用进程空间="

<

结果如下:

整个分配的进程区域被释放了,包括所占的物理内存和页文件。

·何时释放

如果数组的元素大小是小于一个页面4K的话,你需要记录哪些空间不需要,哪些在一个页面上,可以用一个元素一个Bit来记录;另外,你可以创建一个线程定时检测无用单元。

·扩展地址AWE

AWE是内存管理器功能的一套应用程序编程接口(API) ,它使程序能够将物理内存保留为非分页内存,然后将非分页内存部分动态映射到程序的内存工作集。此过程使内存密集型程序(如大型数据库系统)能够为数据保留大量的物理内存,而不必交换分页文件以供使用。相反,数据在工作集中进行交换,并且保留的内存超过 4 GB 范围。

对于物理内存小于2G进程空间时,它的作用是:不必要在物理内存和虚拟页文件中交换。

对于物理内存大于2G进程空间时,它的作用是:应用程序能够访问的物理内存大于2G,也就相当于进程空间超越了2G的范围;同时具有上述优点。

3GB

当在boot.ini 上加上/3GB 选项时,应用程序的进程空间增加了1G,也就是说,你写程序时,可以分配的空间又增大了1G,而不管物理内存是多少,反正有虚拟内存的页文件,大不了慢点。

PAE

当在boot.ini上加上/PAE 选项时,操作系统可以支持大于4G的物理内存,否则,你加再多内存操作系统也是不认的,因为管理这么大的内存需要特殊处理。所以,你内存小于4G 是没有必要加这个选项的。注意,当要支持大于16G的物理内存时,不能使用/3G选项,因为,只有1G的系统空间是不能管理超过16G的内存的。

AWE

当在boot.ini上加上/AWE选项时,应用程序可以为自己保留物理内存,直接的使用物理内存而不通过页文件,也不会被页文件交换出去。当内存大于3G时,就显得特别有用。因为可以充分利用物理内存。

当物理内存大于4G时,需要/PAE的支持。

以下是一个boot.ini的实例图,是我机器上的:

操作系统内存管理复习过程

操作系统内存管理

操作系统内存管理 1. 内存管理方法 内存管理主要包括虚地址、地址变换、内存分配和回收、内存扩充、内存共享和保护等功能。 2. 连续分配存储管理方式 连续分配是指为一个用户程序分配连续的内存空间。连续分配有单一连续存储管理和分区式储管理两种方式。 2.1 单一连续存储管理 在这种管理方式中,内存被分为两个区域:系统区和用户区。应用程序装入到用户区,可使用用户区全部空间。其特点是,最简单,适用于单用户、单任务的操作系统。CP/M和 DOS 2.0以下就是采用此种方式。这种方式的最大优点就是易于管理。但也存在着一些问题和不足之处,例如对要求内

存空间少的程序,造成内存浪费;程序全部装入,使得很少使用的程序部分也占用—定数量的内存。 2.2 分区式存储管理 为了支持多道程序系统和分时系统,支持多个程序并发执行,引入了分区式存储管理。分区式存储管理是把内存分为一些大小相等或不等的分区,操作系统占用其中一个分区,其余的分区由应用程序使用,每个应用程序占用一个或几个分区。分区式存储管理虽然可以支持并发,但难以进行内存分区的共享。 分区式存储管理引人了两个新的问题:内碎片和外碎片。 内碎片是占用分区内未被利用的空间,外碎片是占用分区之间难以利用的空闲分区(通常是小空闲分区)。 为实现分区式存储管理,操作系统应维护的数据结构为分区表或分区链表。表中各表项一般包括每个分区的起始地址、大小及状态(是否已分配)。

分区式存储管理常采用的一项技术就是内存紧缩(compaction)。 2.2.1 固定分区(nxedpartitioning)。 固定式分区的特点是把内存划分为若干个固定大小的连续分区。分区大小可以相等:这种作法只适合于多个相同程序的并发执行(处理多个类型相同的对象)。分区大小也可以不等:有多个小分区、适量的中等分区以及少量的大分区。根据程序的大小,分配当前空闲的、适当大小的分区。 优点:易于实现,开销小。 缺点主要有两个:内碎片造成浪费;分区总数固定,限制了并发执行的程序数目。 2.2.2动态分区(dynamic partitioning)。 动态分区的特点是动态创建分区:在装入程序时按其初始要求分配,或在其执行过程中通过系统调用进行分配或改变分区大小。与固定分区相比较其优点是:没有内碎

实验三动态分区存储管理方式的主

实验三动态分区存储管理方式的主存分配回收 一、实验目的 深入了解动态分区存储管理方式主存分配回收的实现。 二、实验预备知识 存储管理中动态分区的管理方式。 三、实验内容 编写程序完成动态分区存储管理方式的主存分配回收的实现。实验具体包括: 首先确定主存空间分配表;然后采用最优适应算法完成主存空间的分配和回收;最后编写主函数对所做工作进行测试。 四、提示与讲解 动态分区管理方式预先不将主存划分成几个区域,而把主存除操作系统占用区域外的空间看作一个大的空闲区。当作业要求装入主存时,根据作业需要主存空间的大小查询主存内各个空闲区,当从主存空间中找到一个大于或等于该作业大小的主存空闲区时,选择其中一个空闲区,按作业需求量划出一个分区装入该作业。作业执行完后,它所占的主存分区被收回,成为一个空闲区。如果该空闲区的相邻分区也是空闲区,则需要将相邻空闲区合并成一个空闲区。 实现动态分区的分配和回收,主要考虑的问题有三个: 第一,设计记录主存使用情况的数据表格,用来记录空闲区和作业占用的区域;第二,在设计的数据表格基础上设计主存分配算法;第三,在设计的数据表格基础上设计主存回收算法。 首先,考虑第一个问题: 设计记录主存使用情况的数据表格,用来记录空闲区和作业占用的区域。 由于动态分区的大小是由作业需求量决定的,故分区的长度是预先不固定的,且分区的个数也随主存分配和回收变动。总之,所有分区情况随时可能发生变化,数据表格的设计必须和这个特点相适应。由于分区长度不同,因此设计的表格应该包括分区在主

存中的起始地址和长度。由于分配时空闲区有时会变成两个分区: 空闲区和已分分区,回收主存分区时,可能会合并空闲分区,这样如果整个主存采用一张表格记录已分分区和空闲区,就会使表格操作繁琐。主存分配时查找空闲区进行分配,然后填写已分配区表,主要操作在空闲区;某个作业执行完后,将该分区变成空闲区,并将其与相邻的空闲区合并,主要操作也在空闲区。 由此可见,主存的分配和回收主要是对空闲区的操作。这样为了便于对主存空间的分配和回收,就建立两张分区表记录主存使用情况,一张表格记录作业占用分区的 “已分配区表”;一张是记录空闲区的“空闲区表”。这两张表的实现方法一般有两种,一种是链表形式,一种是顺序表形式。在实验中,采用顺序表形式,用数组模拟。由于顺序表的长度必须提前固定,所以无论是“已分配区表”还是“空闲区 表”都必须事先确定长度。它们的长度必须是系统可能的最大项数,系统运行过程中才不会出错,因而在多数情况下,无论是“已分配区表”还是“空闲区表”都有空闲栏目。已分配区表中除了分区起始地址、长度外,也至少还要有一项“标志”,如果是空闲栏目,内容为“空”,如果为某个作业占用分区的登记项,内容为该作业的作业名;空闲区表中除了分区起始地址、长度外,也要有一项“标志”,如果是空闲栏目,内容为“空”,如果为某个空闲区的登记项,内容为“未分配”。在实际系统中,这两表格的内容可能还要多,实验中仅仅使用上述必须的数据。为此, “已分配区表”和“空闲区表”在实验中有如下的结构定义。 已分配区表的定义: #define n 10// 假定系统允许的最大作业数量为n struct {float address;// 已分分区起始地址 float length; // 已分分区长度,单位为字节 int flag;// 已分配区表登记栏标志, “0表”示空栏目,实验中只支持一个字符的作业名}used_table[n];// 已分配区表 空闲区表的定义:

linux内存管理子系统 笔记

4-4 linux内存管理子系统 4-4-1 linux内存管理(参考课件) 物理地址:cpu地址总线上寻址物理内存的地址信号,是地址变换的最终结果 逻辑地址:程序代码经过编译后,出现在汇编程序中的地址(程序设计时使用的地址) 线性地址:又名虚拟地址,32位cpu架构下4G地址空间 CPU要将一个逻辑地址转换为物理地址,需要两步: 1、首先CPU利用段式内存管理单元,将逻辑地址转换成线性地址; 2、再利用页式内存管理单元,把线性地址最终转换为物理地址 相关公式: 逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)(通用的) 16位CPU:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器) 线性地址=段寄存器的值×16+逻辑地址的偏移部分 物理地址=线性地址(没有页式管理) 32位CPU:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器) 线性地址=段寄存器的值+逻辑地址的偏移部分 物理地址<——>线性地址(mapping转换) ARM32位:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器) 逻辑地址=段内偏移量(段基地址为0) 线性地址=逻辑地址=段内偏移量(32位不用乘以32) 物理地址<——>线性地址(mapping转换) ************************!!以下都是x86模式下!!********************************* 一、段式管理 1.1、16位CPU:(没有页式管理) 1.1.1、段式管理的由来: 16位CPU内部有20位地址总线,可寻址2的20次方即1M的内存空间,但16位CPU 只有16位的寄存器,因此只能访问2的16次方即64K。因此就采用了内存分段的管理模式,在CPU内部加入了段寄存器,这样1M被分成若干个逻辑段,每个逻辑段的要求如下: 1、逻辑段的起始地址(段地址)必须是16的整数倍,即最后4个二进制位须全是0 (因此不必保存)。 2、逻辑段的最大容量为64K。 1.1.2、物理地址的形成方式: 段地址:将段寄存器中的数值左移4位补4个0(乘以16),得到实际的段地址。 段偏移:在段偏移寄存器中。 1)逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器) 2)由逻辑地址得到物理地址的公式为:(因为没有页式管理,所以这一步就得到了物理地址)物理地址PA=段寄存器的值×16+逻辑地址的偏移部分(注意!!)(段与段可能会重叠)

四川大学 操作系统上机实验 实验五 Windows虚拟存储器管理

实验报告 实验名称:Windows虚拟存储器管理 实验时间:2013年5月27日 实验人员:____郑笑凡___(姓名)__1143041243__(学号)____2011____(年级) 实验目的:1、了解Windows 2000/XP的内存管理机制,掌握页式虚拟存储技术。 2、理解内存分配原理,特别是以页面为单位的虚拟内存分配方法。 3、学会使用Windows 2000/XP下内存管理的基本API函数 实验环境:windows xp 实验步骤: 1、下载virtumem.cpp; 2、建立工程,将virtumen.cpp加入; 3、编译工程,观察结果,确信六种状态都出现至少一次,必要时可改程 序,方便观察结果; 4、看懂程序,按要求另写一段小程序; 5、编译,执行,观察结果。 6,总结。 实验陈述: 1、基础知识: pagefile.sys文件的位置在:__安装的系统盘根目录下____________________________________此文件的作用:____实现物理内存的扩展__________________________________________________ 改变此文件大小的方法:右击”我的电脑”,依次选择”属性”—“高级”—“性能选项”— “更改”_______________________________________ 虚拟地址空间中的页面分为:提交页面,保留页面,空闲页面 页面的操作可以分为:保留、提交、回收、释放、加锁 2、编程准备. 页面属性是在结构体MEMORY_BASIC_INFORMATION_的字段AllocationProtect 和字段中Protect体现出来的。 简述VirtualFree,VirtualPtotect,VirtualLock,VirtualUnlock,VirtualQuery的作用:_ VirtualFree:__释放虚存___________________________________________________ VirtualPtotect:_保留虚存_________________________________________________ VirtualLock:___加锁虚存_________________________________________________ VirtualUnlock:_解锁虚存________________________________________________ VirtualQuery:____查询虚存_______________________________________________ 3、编程 1)将virtumem.cpp加入工程,编译,执行。 是否能编译成功?是 请描述运行结果:

全面介绍Windows内存管理机制

全面介绍Windows内存管理机制及C++内存分配实例 文章整理: https://www.doczj.com/doc/0817880422.html, 文章来源: 网络- - 本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用;根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制。 本文目的: 对Windows内存管理机制了解清楚,有效的利用C++内存函数管理和使用内存。本文内容: 本文一共有六节,由于篇幅较多,故按节发表。 1.进程地址空间 1.1地址空间 ?32|64位的系统|CPU 操作系统运行在硬件CPU上,32位操作系统运行于32位CPU 上,64位操作系统运行于64位CPU上;目前没有真正的64位CPU。 32位CPU一次只能操作32位二进制数;位数多CPU设计越复杂,软件设计越简单。 软件的进程运行于32位系统上,其寻址位也是32位,能表示的空间是232=4G,范围从0x0000 0000~0xFFFF FFFF。 ?NULL指针分区 范围:0x0000 0000~0x0000 FFFF 作用:保护内存非法访问 例子:分配内存时,如果由于某种原因分配不成功,则返回空指针0x0000 0000;当用户继续使用比如改写数据时,系统将因为发生访问违规而退出。 那么,为什么需要那么大的区域呢,一个地址值不就行了吗?我在想,是不是因为不让8或16位的程序运行于32位的系统上呢?!因为NULL分区刚好范围是16的进程空间。 ?独享用户分区 范围:0x0001 0000~0x7FFE FFFF 作用:进程只能读取或访问这个范围的虚拟地址;超越这个范围的行为都 会产生违规退出。 例子: 程序的二进制代码中所用的地址大部分将在这个范围,所有exe 和dll文件都加载到这个。每个进程将近2G的空间是独享的。 注意:如果在boot.ini上设置了/3G,这个区域的范围从2G扩大为3G: 0x0001 0000~0xBFFE FFFF。 ?共享内核分区 范围:0x8000 0000~0xFFFF FFFF 作用:这个空间是供操作系统内核代码、设备驱动程序、设备I/O高速缓存、非页面内存池的分配、进程目表和页表等。 例子: 这段地址各进程是可以共享的。

操作系统课程设计--连续动态分区内存管理模拟实现

(操作系统课程设计) 连续动态分区内存 管理模拟实现

目录 《操作系统》课程设计 (1) 引言 (3) 课程设计目的和内容 (3) 需求分析 (3) 概要设计 (3) 开发环境 (4) 系统分析设计 (4) 有关了解内存管理的相关理论 (4) 内存管理概念 (4) 内存管理的必要性 (4) 内存的物理组织 (4) 什么是虚拟内存 (5) 连续动态分区内存管理方式 (5) 单一连续分配(单个分区) (5) 固定分区存储管理 (5) 可变分区存储管理(动态分区) (5) 可重定位分区存储管理 (5) 问题描述和分析 (6) 程序流程图 (6) 数据结构体分析 (8) 主要程序代码分析 (9) 分析并实现四种内存分配算法 (11) 最先适应算 (11) 下次适应分配算法 (13) 最优适应算法 (16)

最坏适应算法......................................................... (18) 回收内存算法 (20) 调试与操作说明 (22) 初始界面 (22) 模拟内存分配 (23) 已分配分区说明表面 (24) 空闲区说明表界面 (24) 回收内存界面 (25) 重新申请内存界面..........................................................26. 总结与体会 (28) 参考文献 (28) 引言 操作系统是最重要的系统软件,同时也是最活跃的学科之一。我们通过操作系统可以理解计算机系统的资源如何组织,操作系统如何有效地管理这些系统资源,用户如何通过操作系统与计算机系统打交道。 存储器是计算机系统的重要组成部分,近年来,存储器容量虽然一直在不断扩大,但仍不能满足现代软件发展的需要,因此,存储器仍然是一种宝贵而又紧俏的资源。如何对它加以有效的管理,不仅直接影响到存储器的利用率,而且还对系统性能有重大影响。而动态分区分配属于连续分配的一种方式,它至今仍在内存分配方式中占有一席之地。 课程设计目的和内容: 理解内存管理的相关理论,掌握连续动态分区内存管理的理论;通过对实际问题的编程实现,获得实际应用和编程能力。

Solaris 8内存管理机制研究

Solaris 8内存管理机制研究 吴海燕 戚丽 冯珂 摘 要:寻找性能瓶颈是性能分析中的一项重要任务,内存瓶颈的表现并不像CPU或磁盘那样直接,本文通过对Solaris 8内存管理机制的研究,给出了寻找Solaris 8系统内存瓶颈的方法。 关键词:Solaris 8,内存管理,性能优化 一、问题的提出 清华大学计算机与信息管理中心数据中心现有服务器近百台,其中包括了SUN Fire 15000、SUN Enterprise 5500、SUN Enterprise 5000等大型SUN服务器,Solaris 8是主流操作系统。为了对服务器的资源(如CPU、内存、磁盘、网络)的使用情况进行长期监控,建立性能优化(performance tuning)的基准值,我们开发了一套脚本程序定时采集系统运行参数。在长期的监控中,我们发现Solaris 8系统的空闲内存(freemem)呈现一个有趣的变化规律,如图1所示: 图1 空闲内存(freemem)变化图 图1是某Solaris 8系统(在下文中我们称之为15k-a)自2003年2月份以来的freemem 变化情况,横坐标是时间,纵坐标是freemem的数量,以8K字节为单位。15k-a配置是10路Super SPARCIII CPU,10GB物理内存。从上图可以看到在正常运行时,freemem应该是比较稳定的,15k-a主要是运行数据库,数据库在运行时会占用2G内存作为SGA区使用,因此在通常的负载下,freemem保持在6~7G之间是比较正常的。稳定一段时间后,

15k-a的freemem会持续走低,直到最低值,约为18893×8KMB,然后系统开始回收内存,我们就会看到freemem数量急剧上升。freemem的陡降都发生在凌晨1:00之后,检查系统作业发现每天1:00都会有一个数据库备份脚本开始运行:首先是用“exp”命令给数据库做逻辑备份,然后用“cp”命令把备份出来的文件拷贝到后备存储上。这两个命令都是正常退出,没有任何报错。开始时我们曾怀疑是有内存泄漏,当某一天freemem大幅攀升时,此怀疑被解除了,因为如果有内存泄漏,系统是无法将内存回收回来的。 对于一个物理内存为10GB的系统来说,如果空闲内存(freemem)真的减少到不到二百兆,那将存在着严重的问题。但奇怪的是系统的CPU使用率一直很低,所有进程的反应也很快,系统没有任何资源匮乏的迹象。如何解释这些问题呢,为此我们对Solaris 2.x 的内存管理机制进行了研究。 二、Solaris的内存管理机制 Solaris 8的内存管理为虚拟内存管理。[1]简单地说,虚拟内存就是进程看到比它实际使用的物理内存多得多的内存空间,对于64位的Solaris 8操作系统,进程可以通过8K 大小的段寻址访问2的64次方字节的内存空间,这种8K的段被称为页(page)。传统的UNIX通过进程(pagedaemon)完成虚拟地址和物理地址间的转换,在Solaris中这些是通过一个硬件-MMU(Memory Management Unit)-来实现的。在多处理器系统中,每个CPU 都有自己的MMU。Solaris 8的虚拟存储体系由系统寄存器、CPU CACHE、主存(RAM,物理内存)、外存(磁盘、磁带等)构成。 有两个基本的虚拟内存系统管理模型[2]:交换(swapping)和按需换页(demand paged)模型。交换模型的内存管理粒度是用户进程,当内存不足时,最不活跃的进程被交换出内存(swapping out)。按需换页模型的内存管理粒度是页(page),当内存匮乏时,只有最不经常使用的页被换出。Solaris 8结合使用了这两种内存管理模型,在通常情况下使用按需换页模型,当内存严重不足时,使用交换模型来进行内存释放。 与传统UNIX系统相比,Solaris虚拟内存系统的功能要丰富得多,它负责管理所有与I/O和内存相关的对象,包括内核、用户应用程序、共享库和文件系统。传统的UNIX系统V(System V)使用一个单独的缓冲区来加速文件系统的I/O, Solaris 8则使用虚拟内存系统来管理文件系统的缓存,系统的所有空闲内存都可以被用来做为文件I/O缓存,因为RAM的访问速度比磁盘快得多,所以这样做带来的性能提高是可观的。这也意味着在存在大量文件系统I/O的系统上,空闲内存的数量几乎是0。 了解系统内存被分配到了什么地方,系统在什么情况下进行内存整理是系统管理的重

操作系统实验之内存管理实验报告

学生学号 实验课成绩 武汉理工大学 学生实验报告书 实验课程名称 计算机操作系统 开 课 学 院 计算机科学与技术学院 指导老师姓名 学 生 姓 名 学生专业班级 2016 — 2017 学年第一学期

实验三 内存管理 一、设计目的、功能与要求 1、实验目的 掌握内存管理的相关内容,对内存的分配和回收有深入的理解。 2、实现功能 模拟实现内存管理机制 3、具体要求 任选一种计算机高级语言编程实现 选择一种内存管理方案:动态分区式、请求页式、段式、段页式等 能够输入给定的内存大小,进程的个数,每个进程所需内存空间的大小等 能够选择分配、回收操作 内购显示进程在内存的储存地址、大小等 显示每次完成内存分配或回收后内存空间的使用情况 二、问题描述 所谓分区,是把内存分为一些大小相等或不等的分区,除操作系统占用一个分区外,其余分区用来存放进程的程序和数据。本次实验中才用动态分区法,也就是在作业的处理过程中划分内存的区域,根据需要确定大小。 动态分区的分配算法:首先从可用表/自由链中找到一个足以容纳该作业的可用空白区,如果这个空白区比需求大,则将它分为两个部分,一部分成为已分配区,剩下部分仍为空白区。最后修改可用表或自由链,并回送一个所分配区的序号或该分区的起始地址。 最先适应法:按分区的起始地址的递增次序,从头查找,找到符合要求的第一个分区。

最佳适应法:按照分区大小的递增次序,查找,找到符合要求的第一个分区。 最坏适应法:按分区大小的递减次序,从头查找,找到符合要求的第一个分区。 三、数据结构及功能设计 1、数据结构 定义空闲分区结构体,用来保存内存中空闲分区的情况。其中size属性表示空闲分区的大小,start_addr表示空闲分区首地址,next指针指向下一个空闲分区。 //空闲分区 typedef struct Free_Block { int size; int start_addr; struct Free_Block *next; } Free_Block; Free_Block *free_block; 定义已分配的内存空间的结构体,用来保存已经被进程占用了内存空间的情况。其中pid作为该被分配分区的编号,用于在释放该内存空间时便于查找。size表示分区的大小,start_addr表示分区的起始地址,process_name存放进程名称,next指针指向下一个分区。 //已分配分区的结构体 typedef struct Allocate_Block { int pid; int size; int start_addr; char process_name[PROCESS_NAME_LEN]; struct Allocate_Block *next; } Allocate_Block; 2、模块说明 2.1 初始化模块 对内存空间进行初始化,初始情况内存空间为空,但是要设置内存的最大容量,该内存空间的首地址,以便之后新建进程的过程中使用。当空闲分区初始化

操作系统课程设计内存管理

内存管理模拟 实验目标: 本实验的目的是从不同侧面了解Windows 2000/XP 对用户进程的虚拟内存空间的管理、分配方法。同时需要了解跟踪程序的编写方法(与被跟踪程序保持同步,使用Windows提供的信号量)。对Windows分配虚拟内存、改变内存状态,以及对物理内存(physical memory)和页面文件(pagefile)状态查询的API 函数的功能、参数限制、使用规则要进一步了解。 默认情况下,32 位Windows 2000/XP 上每个用户进程可以占有2GB 的私有地址空间,操作系统占有剩下的2GB。Windows 2000/XP 在X86 体系结构上利用二级页表结构来实现虚拟地址向物理地址的变换。一个32 位虚拟地址被解释为三个独立的分量——页目录索引、页表索引和字节索引——它们用于找出描述页面映射结构的索引。页面大小及页表项的宽度决定了页目录和页表索引的宽度。 实验要求: 使用Windows 2000/XP 的API 函数,编写一个包含两个线程的进程,一个线程用于模拟内存分配活动,一个线程用于跟踪第一个线程的内存行为,而且要求两个线程之间通过信号量实现同步。模拟内存活动的线程可以从一个文件中读出要进行的内存操作,每个内存操作包括如下内容: 时间:操作等待时间。 块数:分配内存的粒度。 操作:包括保留(reserve)一个区域、提交(commit)一个区域、释放(release)一个区域、回收(decommit)一个区域和加锁(lock)与解锁(unlock)一个区域,可以将这些操作编号存放于文件。保留是指保留进程的虚拟地址空间,而不分配物理 存储空间。提交在内存中分配物理存储空间。回收是指释放物理内存空间,但在虚拟地址空间仍然保留,它与提交相对应,即可以回收已经提交的内存块。释放是指将物理存储和虚拟地址空间全部释放,它与保留(reserve)相对应,即可以释放已经保留的内存块。 大小:块的大小。 访问权限:共五种,分别为PAGE_READONLY,PAGE_READWRITE ,PAGE_EXECUTE,PAGE_EXECUTE_READ 和PAGE EXETUTE_READWRITE。可以将这些权限编号存放于文件中跟踪线程将页面大小、已使用的地址范围、物理内存总量,以及虚拟内存总量等信息显示出来。

实验四可变分区存储管理方式的内存分配和回收

实验四 实验四可变分区存储管理方式的内存分配和回收 一.实验目的 通过编写和调试存储管理的模拟程序以加深对存储管理方案的理解,熟悉可变分区存储管理的内存分配和回收。 二.实验属性 设计 三.实验内容 1.确定内存空间分配表; 2.采用最优适应算法完成内存空间的分配和回收; 3.编写主函数对所做工作进行测试。 四.实验背景材料 实现可变分区的分配和回收,主要考虑的问题有三个: 第一,设计记录内存使用情况的数据表格,用来记录空闲区和作业占用的区域;第二,在设计的数据表格基础上设计内存分配算法;第三,在设计的数据表格基础上设计内存回收算法。 首先,考虑第一个问题,设计记录内存使用情况的数据表格,用来记录空间区和作业占用的区域。 由于可变分区的大小是由作业需求量决定的,故分区的长度是预先不固定的,且分区的个数也随内存分配和回收变动。总之,所有分区情况随时可能发生变化,数据表格的设计必须和这个特点相适应。由于分区长度不同,因此设计的表格应该包括分区在内存中的起始地址和长度。由于分配时空闲区有时会变成两个分区:

空闲区和已分分区,回收内存分区时,可能会合并空闲分区,这样如果整个内存采用一张表格记录己分分区和空闲区,就会使表格操作繁琐。分配内存时查找空闲区进行分配,然后填写己分配区表,主要操作在空闲区;某个作业执行完后,将该分区变成空闲区,并将其与相邻的空闲区合并,主要操作也在空闲区。 由此可见,内存的分配和回收主要是对空闲区的操作。这样为了便于对内存空间的分配和回收,就建立两张分区表记录内存使用情况,一张表格记录作业占用分区的“己分分区表”;一张是记录空闲区的“空闲区表”。这两张表的实现方法一般有两种: 一种是链表形式,一种是顺序表形式。在实验中,采用顺序表形式,用数组模拟。由于顺序表的长度必须提前固定,所以无论是“已分分区表”还是“空闲区表”都必须事先确定长度。它们的长度必须是系统可能的最大项数。 “已分分区表”的结构定义 #define n 10 //假定系统允许的最大作业数量为n struct { float address; //已分分区起始地址 float length; //已分分区长度、单位为字节 int flag;//已分分区表登记栏标志,“0”表示空栏目,实验中只支持一个字符的作业名 }used_table[n]; //已分分区表 “空闲区表”的结构定义 #define m 10 //假定系统允许的空闲区最大为m struct { float address; //空闲区起始地址

JVM原理以及JVM内存管理机制

一、 JVM简介 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完成, 首先来说一下JVM工作原理中的jdk这个东西, .JVM 在整个jdk中处于最底层,负责于操作系统的交互,用来屏蔽操作系统环境,提供一个完整的Java运行环境,因此也就虚拟计算机. 操作系统装入JVM是通过jdk中Java.exe来完成。 通过下面4步来完成JVM环境. 1.创建JVM装载环境和配置 2.装载JVM.dll 3.初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例 4.调用JNIEnv实例装载并处理class类。 对于JVM自身的物理结构,我们可以从下图了解:

JVM的一个重要的特征就是它的自动内存管理机制,在执行一段Java代码的时候,会把它所管理的内存划分 成几个不同的数据区域,其中包括: 1. 程序计数器,众所周知,JVM的多线程是通过线程轮流切换并 分配CPU执行时间的方式来实现的,那么每一个线程在切换 后都必须记住它所执行的字节码的行号,以便线程在得到CPU 时间时进行恢复,这个计数器用于记录正在执行的字节码指令的地址,这里要强调的是“字节码”,如果执行的是Native方法,那么这个计数器应该为null; 2.

3. Java计算栈,可以说整个Java程序的执行就是一个出栈入栈 的过程,JVM会为每一个线程创建一个计算栈,用于记录线程中方法的调用和变量的创建,由于在计算栈里分配的内存出栈后立即被抛弃,因此在计算栈里不存在垃圾回收,如果线程请求的栈深度大于JVM允许的深度,会抛出StackOverflowError 异常,在内存耗尽时会抛出OutOfMemoryError异常; 4. Native方法栈,JVM在调用操作系统本地方法的时候会使用到 这个栈; 5. Java堆,由于每个线程分配到的计算栈容量有限,对于可能会 占据大量内存的对象,则会被分配到Java堆中,在栈中包含了指向该对象内存的地址;对于一个Java程序来说,只有一个Java堆,也就是说,所有线程共享一个堆中的对象;由于Java堆不受线程的控制,如果在一个方法结束之后立即回收这个方法使用到的对象,并不能保证其他线程是否正在使用该对象;因此堆中对象的回收由JVM的垃圾收集器统一管理,和某一个线程无关;在HotSpot虚拟机中Java堆被划分为三代:o新生代,正常情况下新创建的对象会被分配到新生代,但如果对象占据的内存足够大以致超过了新生代的容量限 制,也可能被分配到老年代;新生代对象的一个特点是最 新、且生命周期不长,被回收的可能性高;

操作系统实验内存分配

精心整理西安邮电大学 (计算机学院) 课内实验报告 1. (1 (2 (3 原因,写出实验报告。 2.实验要求: 1)掌握内存分配FF,BF,WF策略及实现的思路; 2)掌握内存回收过程及实现思路; 3)参考本程序思路,实现内存的申请、释放的管理程序,调试运行,总结程序设计中出现的问题并找出原因,写出实验报告。

3.实验过程: 创建进程: 删除其中几个进程:(默认以ff首次适应算法方式排列) Bf最佳适应算法排列方式: wf最差匹配算法排列方式: 4.实验心得: 明 实验中没有用到循环首次适应算法,但是对其他三种的描述还是很详细,总的来说,从实验中还是学到了很多。 5.程序源代码: #include #include #include #include

#define PROCESS_NAME_LEN 32 //进程名长度 #define MIN_SLICE 10 //最小碎片的大小#define DEFAULT_MEM_SIZE 1024 //内存大小 #define DEFAULT_MEM_START 0 //起始位置 /*内存分配算法*/ #define MA_FF 1 #define MA_BF 2 #define MA_WF 3 /*描述每一个空闲块的数据结构*/ struct free_block_type { }; /* /* { }; /* /* void display_menu(); int set_mem_size(); void set_algorithm(); void rearrange(int algorithm); int rearrange_WF(); int rearrange_BF(); int rearrange_FF(); int new_process(); int allocate_mem(struct allocated_block *ab);

操作系统实验五 Windows XP 虚拟内存管理

实验五 Windows XP 虚拟内存管理 一实验目的 1) 了解存储器管理以及虚拟存储器管理的基本原理 2)了解和学习Windows系统管理工具中关于内存管理的设置和使用; 二实验环境 需要准备一台运行Windows XP操作系统的计算机。 三背景知识 虚拟存储器技术是当代计算机中广泛采用的内存管理方案,在Windows XP中合理的进行虚拟内存的设置,可以更有效的提高系统的工作效率。利用系统自带的系统监视器可以查看虚拟内存的使用情况,根据使用情况可以灵活的进行虚拟内存的管理。 四实验内容与步骤 启动并进入Windows环境,单击Ctrl + Alt + Del键,或者右键单击任务栏,在快捷菜单中单击“任务管理器”命令,打开“任务管理器”窗口。 步骤1:当前机器中由你打开,正在运行的应用程序有: 1) 5.doc[兼容模式]-Microsoft Word 2) 常州大学-Windows Internet Explorer 3) 常州大学教务单点登录接入平台- Windows Internet Explorer 步骤2:单击“进程”选项卡,一共显示了 33 个进程。请试着区分一下,其中: 系统 (SYSTEM) 进程有 19 个,填入表2-1中。 表2-1 实验记录 映像名称用户名CPU使用率内存使用进程实现的功能Svchost.exe SYSTEM 00 4416K NT Kernel &System Service.exe SYSTEM 00 3272K Windows会话管理器 Sqlservr.ex e SYSTEM 00 9580K Client Server Runtime Process LMS.exe SYSTEM 00 2912K 服务和控制器应用程序MDM.exe SYSTEM 00 3424K Local Security Authority Process Inetinfo.exe SYSTEM 00 9780K 本地会话管理器服务Spoolsv.exe SYSTEM 00 5612K Windows 服务主进程 Ati2evxx.e xe SYSTEM 00 4024K 360主动防御服务模块 Svchost.exe SYSTEM 00 24912K Windows 登录应用程序Svchost.exe SYSTEM 00 5084K Windows 服务主进程Service.exe SYSTEM 00 3476K 服务和控制器应用程序Isass.exe SYSTEM 00 1736K Local Security Authority Process

操作系统内存管理原理

内存分段和请求式分页 在深入i386架构的技术细节之前,让我们先返回1978年,那一年Intel 发布了PC处理器之母:8086。我想将讨论限制到这个有重大意义的里程碑上。如果你打算知道更多,阅读Robert L.的80486程序员参考(Hummel 1992)将是一个很棒的开始。现在看来这有些过时了,因为它没有涵盖Pentium处理器家族的新特性;不过,该参考手册中仍保留了大量i386架构的基本信息。尽管8086能够访问1MB RAM的地址空间,但应用程序还是无法“看到”整个的物理地址空间,这是因为CPU寄存器的地址仅有16位。这就意味着应用程序可访问的连续线性地址空间仅有64KB,但是通过16位段寄存器的帮助,这个64KB大小的内存窗口就可以在整个物理空间中上下移动,64KB逻辑空间中的线性地址作为偏移量和基地址(由16位的段寄存器给处)相加,从而构成有效的20位地址。这种古老的内存模型仍然被最新的Pentium CPU支持,它被称为:实地址模式,通常叫做:实模式。 80286 CPU引入了另一种模式,称为:受保护的虚拟地址模式,或者简单的称之为:保护模式。该模式提供的内存模型中使用的物理地址不再是简单的将线性地址和段基址相加。为了保持与8086和80186的向后兼容,80286仍然使用段寄存器,但是在切换到保护模式后,它们将不再包含物理段的地址。替代的是,它们提供了一个选择器(selector),该选择器由一个描述符表的索引构成。描述符表中的每一项都定义了一个24位的物理基址,允许访问16MB RAM,在当时这是一个很不可思议的数量。不过,80286仍然是16位CPU,因此线性地址空间仍然被限制在64KB。 1985年的80386 CPU突破了这一限制。该芯片最终砍断了16位寻址的锁链,将线性地址空间推到了4GB,并在引入32位线性地址的同时保留了基本的选择器/描述符架构。幸运的是,80286的描述符结构中还有一些剩余的位可以拿来使用。从16位迁移到32位地址后,CPU的数据寄存器的大小也相应的增加了两倍,并同时增加了一个新的强大的寻址模型。真正的32位的数据和地址为程序员带了实际的便利。事实上,在微软的Windows平台真正完全支持32位模型是在好几年之后。Windows NT的第一个版本在1993年7月26日发布,实现了真正意义上的Win32 API。但是Windows 3.x程序员仍然要处理由独立的代码和数据段构成的64KB内存片,Windows NT提供了平坦的4GB地址空间,在那儿可以使用简单的32位指针来寻址所有的代码和数据,而不需要分段。在内部,当然,分段仍然在起作用,就像我在前面提及的那样。不过管理段的所有责任都被移给了操作系统。

实验4内存管理资料讲解

实验 4 内存管理

实验4内存管理 学校:FJUT 学号:3131903229 班级:计算机1302姓名:姜峰 注:其中LFU和NRU算法运行结果可能与其他人不同,只是实现方式不同,基本思路符合就可以。 .实验学时与类型 学时:2,课外学时:自定 实验类型:设计性实验二.实验目的 模拟实现请求页式存储管理中常用页面置换算法,理会操作系统对内存的 调度管理。 三?实验内容 要求:各算法要给出详细流程图以及执行结果截图。 假设有一程序某次运行访问的页面依次是: 0,124,3,4,5,1,2,5,1,2,3,4,5,6 ,请给出采用下列各页面置换算法时页面的换进换出情况,并计算各调度算法的命中率(命中率二非缺页次数/总访问次数),初始物理内存为空,物理内存可在4?20页中选择。 (1)FIFO :最先进入的页被淘汰; (2)LRU :最近最少使用的页被淘汰; (3)OPT :最不常用的页被淘汰;(选做) ⑷LFU :访问次数最少的页被淘汰(LFU)。(选做)

源代码: #i nclude #include #in elude #i nclude #defi ne MAXNUM 100 struct Phy_Memory{ //定义一个物理内存结构体 char Page; int time; }; char *OutPut; struct Phy_Memory *Phy_Page; void Print(char *PageStr,int Phy_PageNum,int absence){ // 打印图解函数int i,j; for(i=0;iPage!=*Temp;i++); if(i

Windows虚拟内存管理

基本概念【摘录】 每个进程都被赋予它自己的虚拟地址空间。对于32位进程来说,这个地址空间是4GB,因为32位指针可以拥有从0x000000000至0xFFFFFFFF之间的任何一个值。这使得一个指针能够拥有4 294 967 296个值中的一个值,它覆盖了一个进程的4GB虚拟空间的范围。这是相当大的一个范围。由于每个进程可以接收它自己的私有的地址空间,因此当进程中的一个线程正在运行时,该线程可以访问只属于它的进程的内存。属于所有其他进程的内存则隐藏着,并且不能被正在运行的线程访问。 注意在Windows 2000中,属于操作系统本身的内存也是隐藏的,正在运行的线程无法访问。这意味着线程常常不能访问操作系统的数据。Windows 98中,属于操作系统的内存是不隐藏的,正在运行的线程可以访问。因此,正在运行的线程常常可以访问操作系统的数据,也可以破坏操作系统(从而有可能导致操作系统崩溃)。在Windows 98中,一个进程的线程不可能访问属于另一个进程的内存。 前面说过,每个进程有它自己的私有地址空间。进程A可能有一个存放在它的地址空间中的数据结构,地址是0x12345678,而进程B则有一个完全不同的数据结构存放在它的地址空间中,地址是0x12345678。当进程A中运行的线程访问地址为0x12345678的内存时,这些线程访问的是进程A的数据结构。当进程B中运行的线程访问地址为0x12345678的内存时,这些线程访问的是进程B的数据结构。进程A中运行的线程不能访问进程B的地址空间中的数据结构。反之亦然。 记住,这是个虚拟地址空间,不是物理地址空间。该地址空间只是内存地址的一个范围。在你能够成功地访问数据而不会出现违规访问之前,必须赋予物理存储器,或者将物理存储器映射到各个部分的地址空间。 每个进程的虚拟地址空间都要划分成各个分区。地址空间的分区是根据操作系统的基本实现方法来进行的。不同的Windows内核,其分区也略有不同。

相关主题
文本预览
相关文档 最新文档