32位应用程序如何使用大于2GB的内存?

您所在的位置:网站首页 2gb内存指的是2*230字节的内存对吗 32位应用程序如何使用大于2GB的内存?

32位应用程序如何使用大于2GB的内存?

2024-07-15 11:47| 来源: 网络整理| 查看: 265

32位应用程序如何使用大于2GB的内存?

不管是在 32 位 Windows 还是在 64 位 Windows,32 位应用程序都只能使用最大 2GB 的内存,这是我们司空见惯的一个设定。但其实 Windows 提供了一些方法让我们打破这样的设定,使程序使用大于 2GB 的内存。

为什么 32 位程序只能使用最大 2GB 内存?

32 位寻址空间只有 4GB 大小,于是 32 位应用程序(进程)最大只能用到 4GB 的内存。然而,除了应用程序本身要用内存,操作系统内核也需要使用。应用程序使用的内存空间分为用户空间和内核空间,每个 32 位程序的用户空间可独享前 2GB 空间(指针值为正数),而内核空间为所有进程共享 2GB 空间(指针值为负数)。所以,32 位应用程序实际能够访问的内存地址空间最多只有 2GB。

让 32 位程序使用大于 2GB 内存的两种方法

editbin

这是 Visual Studio 2017 采用的做法。我们需要使用到两个工具——editbin 和 dumpbin。前者用于编辑我们编译生成好的程序使之头信息中声明支持大于 2GB 内存,后者用于查看程序的头信息验证我们是否改好了。

编辑一个程序使之声明支持大于 2GB 内存的命令是:

editbin /largeaddressaware xxx.exe

其中,xxx.exe 是我们准备修改的程序,可以使用相对路径或绝对路径(如果路径中出现空格记得带引号)。

验证这个程序是否改好了的命令是:

dumpbin /headers xxx.exe | more

同样,xxx.exe 是我们刚刚改好准备检查的程序,可以使用相对路径或绝对路径。

editbin 改之前和改之后用 dumpbin 查看我们的程序头信息,得到下面两张图:

注意到 FILE HEADER VALUES 块的倒数第二行多出了 Application can handle large (>2GB) addresses。

如果没发现,一定是你命令执行中发生了错误,检查一下吧!最容易出现的错误是执行后发现根本就没有这个命令。是的,editbin 命令从哪里来呢?可以在开始菜单中的 Visual Studio 文件夹中查找 Developer Command Prompt for VS 2017,运行这个启动的命令行中就带有 editbin 和 dumpbin。

如果希望能够在 Visual Studio 编译的时候自动调用这个工具,请参见:LargeAddressAware Visual Studio 2015 C#。

编译成 AnyCPU (Prefer 32-bit)

这是本文更推荐的做法,也是最简单的做法。方法是打开入口程序集的属性页,将“目标平台”选为“AnyCPU”,然后勾选“首选 32 位”。需要注意的是,这种生成方式是 .Net Framework 4.5 及以上版本才提供的。

至于 AnyCPU (Prefer 32-bit) 和 x86 两种生成方式的区别,请参见:What is the purpose of the “Prefer 32-bit” setting in Visual Studio 2012 and how does it actually work?。

声明支持大于 2GB 内存后,能使用多少内存?

对于 32 位操作系统,程序依然只能使用 2GB 内存,除非开启了 /3GB 开关,开启方法详见:/3GB。开启后,应用程序的用户态将可以使用 3GB 内存,但内核态将只能使用 1GB 内存。微软认为,是否打开 /3GB 开关是计算机设备开发商需要做的事情,开发商也需要自己测试开启后驱动程序的性能表现和稳定性。

对于 64 位操作系统,Windows 将很豪放地将 4GB 全部贡献给这样的程序,因为系统自己已经有更多的内存寻址空间可以使用了,没必要跟 32 位应用程序抢占寻址空间。

AWE(Address Windowing Extensions)

AWE是Windows的内存管理功能的一组扩展,它允许应用程序获取物理内存,然后将非分页内存的视图动态映射到32位地址空间。虽然32位地址空间限制为4GB,但是非分页内存却可以远远大于4GB。这使需要大量内存的应用程序(如大型数据库系统)能使用的内存量远远大于32位地址空间所支持的内存量。

为了使用大容量内存,除了要用到AWE外,还有一样东西不能少,那就是PAE(Physical Address Extension)。PAE是基于x86的服务器的一种功能,它使运行Windows Server 2003,Enterprise Edition 和Windows Server 2003,Datacenter Edition 的计算机可以支持 4 GB 以上物理内存。物理地址扩展(PAE)允许将最多64 GB的物理内存用作常规的4 KB页面,并扩展内核能使用的位数以将物理内存地址从 32扩展到36。 一般情况下,Windows系统的PAE没有生效,只有开启了PAE后windows系统才可以识别出4G以上的内存。在使用boot.int的系统中,要启动PAE必须在boot.ini中加入/PAE选项。在Windows Vista和Windows7中则必须修改内核文件,同时设置BCD启动项。针对Vista系统和Win7系统可以使用Ready For 4GB这个软件直接完成这一操作,具体方法见Ready For 4GB的软件说明。以下就是一个开启了/PAE选项的boot.ini文件示例:

[boot loader] timeout=30 default=multi(0)disk(0)rdisk(0)partition(1)WINDOWS [operating systems] multi(0)disk(0)rdisk(0)partition(1)WINDOWS="Windows Server 2003, Enterprise" /fastdetect /PAE

本文将以Windows 7旗舰版为例介绍如何在打开PAE的情况下使用AWE在程序中达到使用2G以上内存的目的。下图分别为开启PAE和未开启PAE时系统识别出的内存容量区别。 

开启PAE

关闭PAE

如果没有打开PAE,系统只能认出3G的内存,最多可以再多0.5G不到,这样即使使用AWE,由于系统和其他应用程序已经占去了一部分内存,剩下的内存或许也只有2G多一点了,没什么太大提高。只有当系统认出了4G以上的内存,AWE才能发挥它真正的作用。

下面我们看看windows中给出的有关AWE的API函数,它们都定义在winbase.h中。

#if (_WIN32_WINNT >= 0x0500) // // Very Large Memory API Subset // WINBASEAPI BOOL WINAPI AllocateUserPhysicalPages( __in HANDLE hProcess, __inout PULONG_PTR NumberOfPages, __out_ecount_part(*NumberOfPages, *NumberOfPages) PULONG_PTR PageArray ); WINBASEAPI BOOL WINAPI FreeUserPhysicalPages( __in HANDLE hProcess, __inout PULONG_PTR NumberOfPages, __in_ecount(*NumberOfPages) PULONG_PTR PageArray ); WINBASEAPI BOOL WINAPI MapUserPhysicalPages( __in PVOID VirtualAddress, __in ULONG_PTR NumberOfPages, __in_ecount_opt(NumberOfPages) PULONG_PTR PageArray ); //... #endif

从winbase.h中的定义可以看出,只有当你的系统版本大于或等于0x0500时,才能够使用AWE。

各个版本的_WIN32_WINNT值见下表,Windows 2000以下的版本不能使用AWE。

Minimum system required

Minimum value for _WIN32_WINNT and WINVER

Windows 7

0x0601

Windows Server 2008

0x0600

Windows Vista

0x0600

Windows Server 2003 with SP1, Windows XP with SP2

0x0502

Windows Server 2003, Windows XP

0x0501

Windows 2000

0x0500

如果你的系统版本符合要求,但是编译器在编译加入了AWE API的代码出错,可以在程序头文件中加入下面的代码。

#ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0501 #endif

下面简要介绍一下每个API的功能。

BOOL WINAPI AllocateUserPhysicalPages( //分配物理内存页,用于后面AWE的内存映射 __in HANDLE hProcess, //指定可以使用此函数分配的内存页的进程 __inout PULONG_PTR NumberOfPages, //分配的内存页数,页的大小由系统决定 __out PULONG_PTR UserPfnArray //指向存储分配内存页帧成员的数组的指针 ); BOOL WINAPI FreeUserPhysicalPages( //释放AllocateUserPhysicalPages函数分配的内存 __in HANDLE hProcess, //释放此进程虚拟地址空间中的分配的内存页 __inout PULONG_PTR NumberOfPages, //要释放的内存页数 __in PULONG_PTR UserPfnArray //指向存储内存页帧成员的数组的指针 ); BOOL WINAPI MapUserPhysicalPages( //将分配好的内存页映射到指定的地址 __in PVOID lpAddress, //指向要重映射的内存区域的指针 __in ULONG_PTR NumberOfPages, //要映射的内存页数 __in PULONG_PTR UserPfnArray //指向要映射的内存页的指针 );

在看实例程序前还有一些设置需要做,需要对系统的本地安全策略进行设置。在win7中,打开“控制面板->系统和安全->管理工具->本地安全策略”,给“锁定内存页”添加当前用户,然后退出,重启(不重启一般无法生效!)。

经过前面的准备(再啰嗦一次:确认自己的电脑装有4G或4G以上的内存;开启PAE,使系统认出4G或以上的内存;设置好本地安全策略),我们就可以通过下面的代码来做个实验了。

代码是从MSDN中AWE的一个Example修改而来的,具体流程见代码中的注释,如果对该Example的源代码有兴趣可以参考MSDN。

#include "AWE_TEST.h" #include #include #define MEMORY_REQUESTED ((2*1024+512)*1024*1024) //申请2.5G内存,测试机上只有4G内存,而且系统是window7,比较占内存.申请3G容易失败. #define MEMORY_VIRTUAL 1024*1024*512 //申请长度0.5G的虚拟内存,即AWE窗口. //检测"锁定内存页"权限的函数 BOOL LoggedSetLockPagesPrivilege ( HANDLE hProcess, BOOL bEnable); void _cdecl main() { BOOL bResult; // 通用bool变量 ULONG_PTR NumberOfPages; // 申请的内存页数 ULONG_PTR NumberOfPagesInitial; // 初始的要申请的内存页数 ULONG_PTR *aPFNs; // 页信息,存储获取的内存页成员 PVOID lpMemReserved; // AWE窗口 SYSTEM_INFO sSysInfo; // 系统信息 INT PFNArraySize; // PFN队列所占的内存长度 GetSystemInfo(&sSysInfo); // 获取系统信息 printf("This computer has page size %d./n", sSysInfo.dwPageSize); //计算要申请的内存页数. NumberOfPages = MEMORY_REQUESTED/sSysInfo.dwPageSize; printf ("Requesting %d pages of memory./n", NumberOfPages); // 计算PFN队列所占的内存长度 PFNArraySize = NumberOfPages * sizeof (ULONG_PTR); printf ("Requesting a PFN array of %d bytes./n", PFNArraySize); aPFNs = (ULONG_PTR *) HeapAlloc(GetProcessHeap(), 0, PFNArraySize); if (aPFNs == NULL) { printf ("Failed to allocate on heap./n"); return; } // 开启"锁定内存页"权限 if( ! LoggedSetLockPagesPrivilege( GetCurrentProcess(), TRUE ) ) { return; } // 分配物理内存,长度2.5GB NumberOfPagesInitial = NumberOfPages; bResult = AllocateUserPhysicalPages( GetCurrentProcess(), &NumberOfPages, aPFNs ); if( bResult != TRUE ) { printf("Cannot allocate physical pages (%u)/n", GetLastError() ); return; } if( NumberOfPagesInitial != NumberOfPages ) { printf("Allocated only %p pages./n", NumberOfPages ); return; } // 保留长度0.5GB的虚拟内存块(这个内存块即AWE窗口)的地址 lpMemReserved = VirtualAlloc( NULL, MEMORY_VIRTUAL, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE ); if( lpMemReserved == NULL ) { printf("Cannot reserve memory./n"); return; } char *strTemp; for (int i=0;i后期生成事件命令行中设置如下命令行:

  call"%VS110COMNTOOLS%..\tools\vsvars32.bat"

  editbin /largeaddressaware$(TargetPath)

 说明:方法一在每次编译后需要执行相关命令;方法二在VS编译后自动完成相关设置操作,使用方便。

2.2.2  测试程序

以下为测试程序的编写示例:

测试程序:通过在exe中进行编写以下大量分配内存的程序进行测试,将错误信息输出到日志

static void Main(string[] args) { try { int count = 100000000; List lst = newList(count); for (int i = 0; i < count;i++) { lst.Add(new int[1000000]); } } catch (Exception ex) { LogHelper.Error.Append(ex); } }

1)未突破2G限制的日志信息如下:

19:33:07:849

System.OutOfMemoryException:引发类型为“System.OutOfMemoryException”的异常。

19:33:07:855

当前专用内大小:1.69 GB

当前虚拟内存大小:1.94 GB

最大虚拟内存量:1.94 GB

物理内存总大小:4 GB

可用物理内存大小:4 GB

虚拟内存总大小:2 GB

可用虚拟内存大小:62.31 MB

交换空间总大小:4 GB

可用交换空间大小:4 GB

2)突破4G限制的日志信息如下:

19:33:45:371

System.OutOfMemoryException:引发类型为“System.OutOfMemoryException”的异常。

19:33:45:377

当前专用内大小:3.59 GB

当前虚拟内存大小:3.9 GB

最大虚拟内存量:3.91 GB

物理内存总大小:4 GB

可用物理内存大小:4 GB

虚拟内存总大小:4 GB

可用虚拟内存大小:105.38 MB

交换空间总大小:4 GB

可用交换空间大小:4 GB

结论

综上,通过方案一和方案二相结合,能比较完美地解决32位程序内存溢出的问题(当然,如果这个程序使用过程中占用的内存达到3G以上的时候还是容易出问题,那就需要从程序本身去优化了)。

【转载】https://docs.microsoft.com/en-us/troubleshoot/windows-server/performance/desktop-heap-limitation-out-of-memory

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3