使用windows内存-VirtualAlloc

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

·        虚拟内存使用场合

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

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

 

·        分配虚拟内存

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


·        保留

用以下Windows 函数保留内存块

    分配的最大空间小于用户区间(通常是2G)。

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_READWRITE); 

            if(pV==NULL)

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

            MEMORYSTATUS memStatusVirtual1;

            GlobalMemoryStatus(&memStatusVirtual1);

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

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

cout<<"减少物理内存="<<memStatusVirtual.dwAvailPhys-memStatusVirtual1.dwAvailPhys<<endl;

cout<<"减少可用页文件="<<memStatusVirtual.dwAvailPageFile-memStatusVirtual1.dwAvailPageFile<<endl;

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

<<memStatusVirtual.dwAvailVirtual-memStatusVirtual1.dwAvailVirtual<<endl<<endl;

结果如下:

 

可见,进程空间减少了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<<"没有那么多物理空间!"<<endl;

            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<<"初始值="<<iP[0]<<endl;//可以访问

 

·        清除物理存储器内容

清除页面指的是,将页面清零,也就是说当作页面没有改变。假设数据存在物理内存中,系统没有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<<"清除失败!"<<endl;

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

C++程序:

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

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

            for(int i=0;i<memStatus.dwAvailPhys+10000000;i++)

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

            GlobalMemoryStatus(&memStatus);

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

            cout<<"长度="<<memStatus.dwLength<<endl;

            cout<<"内存繁忙程度="<<memStatus.dwMemoryLoad<<endl;

            cout<<"总物理内存="<<memStatus.dwTotalPhys<<endl;

            cout<<"可用物理内存="<<memStatus.dwAvailPhys<<endl;

            cout<<"总页文件="<<memStatus.dwTotalPageFile<<endl;

            cout<<"可用页文件="<<memStatus.dwAvailPageFile<<endl;

            cout<<"总进程空间="<<memStatus.dwTotalVirtual<<endl;

            cout<<"可用进程空间="<<memStatus.dwAvailVirtual<<end;

   cout<<"清除后="<<iP[0]<<endl;

结果如下:

使用windows内存-VirtualAlloc_第1张图片

 

当内存所剩无几时,系统将刚清除的内存页面分配出去,同时不会把页面的内存写到虚拟页面文件中。可以看见,原先是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<<"物理内存释放:"<<endl;

cout<<"增加物理内存="<<memStatusVirtual3.dwAvailPhys-memStatusVirtual2.dwAvailPhys<<endl;

cout<<"增加可用页文件="<<memStatusVirtual3.dwAvailPageFile-memStatusVirtual2.dwAvailPageFile<<endl;

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

<<memStatusVirtual3.dwAvailVirtual-memStatusVirtual2.dwAvailVirtual<<endl<<endl;

结果如下:

 

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

 

释放整个保留的空间:

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

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

C++程序:

VirtualFree(pV,0,MEM_RELEASE);

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

 

            MEMORYSTATUS memStatusVirtual4;

            GlobalMemoryStatus(&memStatusVirtual4);

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

cout<<"增加物理内存="<<memStatusVirtual4.dwAvailPhys-memStatusVirtual3.dwAvailPhys <<endl;

cout<<"增加可用页文件="<<memStatusVirtual4.dwAvailPageFile-memStatusVirtual3.dwAvailPageFile<<endl;

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

<<memStatusVirtual4.dwAvailVirtual-memStatusVirtual3.dwAvailVirtual<<endl<<endl;

结果如下:

 

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


实例源码:

#include "stdafx.h"

#include "windows.h"

#include "conio.h"

#include <iostream>

using namespace std;

void MemoryStatus()

{

        MEMORYSTATUSmemStatus; 

        GlobalMemoryStatus(&memStatus); 

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

        cout<<"内存繁忙程度="<<memStatus.dwMemoryLoad<<endl; 

        cout<<"总物理内存="<<memStatus.dwTotalPhys<<endl; 

        cout<<"可用物理内存="<<memStatus.dwAvailPhys<<endl; 

        cout<<"总页文件="<<memStatus.dwTotalPageFile<<endl; 

        cout<<"可用页文件="<<memStatus.dwAvailPageFile<<endl; 

        cout<<"总进程空间="<<memStatus.dwTotalVirtual<<endl; 

        cout<<"可用进程空间="<<memStatus.dwAvailVirtual<<endl; 

}

 

int _tmain(int argc, _TCHAR*argv[])

{

        cout<< "初始状态:" << endl;

        MemoryStatus();

        //申请内存

        char*lpv = (char*)VirtualAlloc(NULL, 1024*1024*1024, MEM_RESERVE,

               PAGE_READWRITE);

        cout<< "申请内存后:" << endl;

        MemoryStatus();

        //提交内存

        lpv= (char*)VirtualAlloc(lpv, 1024*1024*1024, MEM_COMMIT,

               PAGE_READWRITE);

        cout<< "提交内存后:" << endl;

        MemoryStatus();

        //使用内存

        strcpy(lpv,"abc");

        cout<< "使用内存后:" << endl;

        MemoryStatus();

        //释放内存

        VirtualFree(lpv,1024*1024*1024, MEM_RELEASE);

 

        system("pause");

        return0;

}

执行结果:

使用windows内存-VirtualAlloc_第2张图片

使用windows内存-VirtualAlloc_第3张图片

由执行结果可知:

申请内存后,只减少了可用进程空间,提交内存后,减少了可用页文件,使用内存后,减少了实际物理内存


你可能感兴趣的:(内存,VirtualAlloc)