> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# XMemVirtualAlloc

> XMemVirtualAlloc

# XMemVirtualAlloc

在调用进程的虚拟地址空间中预留、提交或更改一个区域的页状态。

<Note>
  与标准 Win32 <a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc" target="_blank">VirtualAlloc</a> 不同，此函数分配的内存*不*保证会自动初始化为零。
</Note>

## 语法

```cpp theme={null}
PVOID XMemVirtualAlloc(  
         PVOID BaseAddress,  
         SIZE_T Size,  
         DWORD AllocationType,  
         ULONGLONG XMemFlags,  
         DWORD PageProtection  
)  
```

### 参数

*BaseAddress*   \_In\_opt\_<br />类型：PVOID

要分配的区域的起始地址。如果正在预留内存，则指定的地址会向下舍入到最接近的分配粒度的倍数（在 XBOX 上为 4K、64K 或 2MB）。如果内存已被预留且正在提交，则地址会向下舍入到下一个页边界。如果此参数为 NULL，则系统会决定在哪里分配该区域。

<br />

*Size*   \_In\_<br />类型：SIZE\_T

区域的大小（以字节为单位）。如果 *BaseAddress* 参数为 NULL，则此值将向上舍入到下一个页边界。否则，分配的页包含从 *BaseAddress* 到 *BaseAddress*+*Size* 范围内包含一个或多个字节的所有页。这意味着跨越页边界的 2 字节范围会使两个页都包含在分配的区域中。

<br />

*AllocationType*   \_In\_<br />类型：DWORD

内存分配的类型。此参数必须包含以下值之一。

| 值 | 含义 | |
| - | - | - |
| **MEM\_COMMIT**<br />0x00001000 | 为指定的预留内存页分配内存开销（从总体内存大小中）。若要一步预留并提交页，请使用 \`MEM\_COMMIT | MEM\_RESERVE\` 调用 **XMemVirtualAlloc**。<br />尝试通过指定 **MEM\_COMMIT**（不指定 **MEM\_RESERVE**）和非 NULL 的 *BaseAddress* 来提交特定地址范围，除非整个范围已被预留，否则将失败。生成的错误代码为 **ERROR\_INVALID\_ADDRESS**。<br />尝试提交已提交的页不会导致函数失败。这意味着你可以提交页，而不必先确定每个页的当前提交状态。 |
| **MEM\_RESERVE**<br />0x00002000 | 预留一段进程虚拟地址空间的范围，而不在内存中分配任何实际的物理存储。你可以在后续对 **XMemVirtualAlloc** 函数的调用中提交预留的页，或直接使用 **XMemMapPhysicalPages** 映射物理页。<br />其他内存分配函数（如 **malloc** 和 **new**）在预留的内存范围被释放之前无法使用它。<br /><br />请注意，XBOX 上 *Microsoft Game Development Kit (GDK)* 中的新内存管理器要求在预留时提供页保护和缓存一致性设置，这与之前的 XBOX OS 版本不同。 | |
| **MEM\_RESERVE\_PLACEHOLDER**<br />0x00040000 | 占位符预留虚拟地址，但不会将该 VA 锁定到某个页大小或页保护。页大小和页保护的选择在使用 **MEM\_REPLACE\_PLACEHOLDER** 将占位符替换为真正的预留或提交时选择。若要创建占位符，请使用 **MEM\_RESERVE \| MEM\_RESERVE\_PLACEHOLDER** 调用 **XMemVirtualAlloc** 并将 PageProtection 设置为 **PAGE\_NOACCESS**。使用 **MEM\_RESERVE\_PLACEHOLDER** 总是会返回一个 2MB 对齐的地址，以确保将来可以与 **MEM\_2MB\_PAGES** 一起使用。但是，一旦预留，占位符可以使用 **MEM\_RELEASE \| MEM\_PRESERVE\_PLACEHOLDER** 的 VirtualFree 在任意粒度（4K、64K 或 2MB）下拆分。 | |
| **MEM\_REPLACE\_PLACEHOLDER**<br />0x00004000 | 将占位符替换为普通的私有分配。当你替换占位符时，BaseAddress 和 Size 必须与占位符完全匹配。将占位符替换为私有分配后，若要将该分配释放回占位符，请参阅 **VirtualFree** 和 **VirtualFreeEx** 的 **dwFreeType** 参数。 | |

此参数还可以按如下方式指定以下值。

| 值 | 含义 |
| - | - |
| **MEM\_2MB\_PAGES**<br />0x20000000 | 指定 2MB 的页大小。 |
| **MEM\_64K\_PAGES**<br />0x20400000 | 指定 64K 的页大小。 |

<Note>
  对于 ERA 游戏，**MEM\_LARGE\_PAGES** 表示 64K 页，而在 Windows 桌面上表示 2MB 页。由于存在歧义，该常量已在 *Microsoft Game Development Kit (GDK)* 中移除。
</Note>

<br />

*XMemFlags*   \_In\_<br />类型：ULONGLONG

在 XBOX 上用于获取某些专用内存类型。此参数必须包含以下值之一。

| 值 | 含义 |
| - | - |
| **XMEM\_CPU**<br />0x00000001 | 分配只能由 CPU 访问的内存。 |
| **XMEM\_GRAPHICS**<br />0x00000003 | 分配可由 CPU 和 GPU 同时访问的内存。使用 **XMEM\_GRAPHICS** 提交的任何内存将被映射到 GPU 的页表中。 |
| **XMEM\_GRAPHICS\_32BIT\_ADDRESS**<br />0x00000007 | 指定自动分配的地址位于地址空间的低 4GB 内。使用此标志提交的内存也可由 GPU 访问。 |

在 XBOX 上，可以分配具有受限虚拟内存地址的内存，保证不使用高位。使用以下任一值将限制地址仅使用所指示的位数。**XMEM\_40BIT\_ADDRESS** (0x00007000ULL)、**XMEM\_41BIT\_ADDRESS** (0x00006000ULL)、**XMEM\_42BIT\_ADDRESS** (0x00005000ULL)、**XMEM\_43BIT\_ADDRESS** (0x00004000ULL)、**XMEM\_44BIT\_ADDRESS** (0x00003000ULL)、**XMEM\_45BIT\_ADDRESS** (0x00002000ULL)、**XMEM\_46BIT\_ADDRESS** (0x00001000ULL)。

<Note>
  在 D3D12Device 创建或调用 D3DConfigureVirtualMemory 之前，无法分配图形内存。如果请求的内存对 GPU 可见，则游戏*必须*同时指定显式的图形页保护标志，不再有基于 CPU 页保护设置的默认值。这与之前的 XBOX OS 版本不同。
</Note>

此参数还可以按如下方式指定以下值。

| 值 | 含义 |
| - | - |
| **XMEM\_TOOL**<br />0x00000010 | 指定分配计入工具分区而不是游戏分区。此标志要求在 *AllocationType* 参数上指定 **MEM\_2MB\_PAGES** 或 **MEM\_64K\_PAGES**。 |
| **XMEM\_MAPPABLE**<br />0x00000020 | 指定该分配是一个地址预留，用于通过 [XMemMapPhysicalPages](/zh-CN/reference/system/xmem/functions/xmemmapphysicalpages) 进行物理页映射。此标志要求在 *AllocationType* 参数上指定 **MEM\_RESERVE** 以及 **MEM\_2MB\_PAGES** 或 **MEM\_64K\_PAGES**。 |

在 Anaconda 设备以及仿真 Anaconda 的 Dante 设备上，在使用高级游戏内存模式时，还可以应用以下参数。在所有其他情况下，将忽略这些参数。

<Note>
  这些标志是可选的。如果未指定，所有图形分配将默认使用 XMEM\_GPU\_OPTIMAL\_BANDWIDTH\_PREFERRED，而所有其他分配将默认使用 XMEM\_STANDARD\_BANDWIDTH\_PREFERRED。
</Note>

| 值 | 含义 |
| - | - |
| **XMEM\_STD\_BW\_REQ** 或 <br />**XMEM\_STANDARD\_BANDWIDTH\_REQUIRED**<br />0x00000080 | 指定必须使用标准带宽内存来满足分配。 |
| **XMEM\_STD\_BW\_PREF** 或 <br />**XMEM\_STANDARD\_BANDWIDTH\_PREFERRED**<br />0x00000180 | 指定分配优先使用标准带宽内存，仅当标准带宽内存耗尽时才使用 GPU 最佳带宽内存。 |
| **XMEM\_GPU\_OPTBW\_REQ** 或 <br />**XMEM\_GPU\_OPTIMAL\_BANDWIDTH\_REQUIRED**<br />0x00000280 | 指定必须使用 GPU 最佳带宽内存来满足分配。 |
| **XMEM\_GPU\_OPTBW\_PREF** 或 <br />**XMEM\_GPU\_OPTIMAL\_BANDWIDTH\_PREFERRED**<br />0x00000380 | 指定分配优先使用 GPU 最佳带宽内存，仅当 GPU 最佳带宽内存耗尽时才使用标准带宽内存。 |

*XMemFlags* 参数包含一个可选的四字符代码标签。字符可以是 a-z、A-Z、0-9 和 \$ 中的任意字符。可以使用函数 [XMemMakeTag](/zh-CN/reference/system/xmem/functions/xmemmaketag) 在正确的位位置构造正确编码的标签值。该标签只能与已提交的内存区域关联，不能与预留关联。此四字符代码标签将由参数的高 24 位表示。可以通过 [XMemVirtualQuery](/zh-CN/reference/system/xmem/functions/xmemvirtualquery) 检索该标签，并且它也会显示在 PIX 内存分析中。

示例：

```cpp theme={null}
PVOID mem = XMemVirtualAlloc(NULL, size, MEM_64K_PAGES | MEM_COMMIT, XMemMakeTag('Fred') | XMEM_CPU, PAGE_READWRITE);
```

<br />

*PageProtection*   \_In\_<br />类型：DWORD

要分配的页区域的内存保护。如果正在提交页，则可以指定 <a href="https://learn.microsoft.com/en-us/windows/desktop/Memory/memory-protection-constants">内存保护常量</a> 中的任何一个。

以下 XBOX 特定的值允许用于 *PageProtection* 参数，并且当指定 **XMEM\_GRAPHICS** 或 **XMEM\_GRAPHICS\_32BIT\_ADDRESS** 时必需其一。此要求与之前的 XBOX OS 版本不同。这些值也可以指定给其他内存管理函数（例如 <a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualprotect">VirtualProtect</a>），用于以 **XMEM\_GRAPHICS** 分配的范围内的内存地址。

| 值 | 含义 |
| - | - |
| **PAGE\_GRAPHICS\_NOACCESS**<br />0x0800 | 指定内存在 GPU 上不可访问。 |
| **PAGE\_GRAPHICS\_READONLY**<br />0x1000 | 指定内存在 GPU 上为只读。 |
| **PAGE\_GRAPHICS\_READWRITE**<br />0x2000 | 指定内存在 GPU 上为可读写。 |
| **PAGE\_GRAPHICS\_EXECUTE**<br />0x4000<br /><br />**GRAPHICS\_EXECUTE\_READ**<br />0x8000<br /><br />**PAGE\_GRAPHICS\_EXECUTE\_READWRITE**<br />0x10000 | 指定内存在 GPU 上可执行。这对于命令缓冲区是必需的。 |

此参数还可以按如下方式指定以下值。

| 值 | 含义 |
| - | - |
| **PAGE\_GRAPHICS\_COHERENT**<br />0x20000 | 指定与 GPU 共享的内存在 CPU 和 GPU 缓存之间保持一致。 |
| **PAGE\_GRAPHICS\_NOCACHE**<br />0x40000 | 指定内存在 GPU 访问时不缓存。此标志仅适用于 XBOX Series X\|S 主机。 |

### 返回值

类型：PVOID

如果函数成功，返回值是所分配的页区域的基地址。

如果函数失败，返回值为 **NULL**。若要获取扩展错误信息，请调用 <a href="https://msdn.microsoft.com/library/windows/desktop/ms679360(v=vs.85).aspx">GetLastError</a>。

## 备注

每个页都有一个关联的<a href="https://learn.microsoft.com/en-us/windows/desktop/Memory/page-state">页状态</a>。**XMemVirtualAlloc** 函数可执行以下操作：

* 提交一个预留页区域
* 预留一个空闲页区域
* 同时预留并提交一个空闲页区域

**XMemVirtualAlloc** 不能预留已预留的页。它可以提交已提交的页。这意味着你可以提交一系列页，无论它们是否已被提交，函数都不会失败。

你可以使用 **XMemVirtualAlloc** 预留一块页，然后再次调用 **XMemVirtualAlloc** 从预留块中提交单个页。这使进程能够预留虚拟地址空间的一个范围，而无需消耗物理存储，直到需要时为止。

如果 *BaseAddress* 参数不为 **NULL**，则该函数使用 *BaseAddress* 和 *Size* 参数来计算要分配的页区域。整个页范围的当前状态必须与 *AllocationType* 参数指定的分配类型兼容。否则，函数会失败并且不会分配任何页。如前所述，此兼容性要求并不排除提交已提交的页。

**XMemVirtualAlloc** 函数可用于在调用进程的虚拟地址空间中预留一个用于映射物理页的内存区域。然后，可以根据应用程序的需要将该内存区域用于将物理页映射进出虚拟内存。必须在 *AllocationType* 参数中设置 **MEM\_RESERVE** 和 **MEM\_64K\_PAGES** 值；不得设置 **MEM\_COMMIT** 值。必须在 *XMemFlags* 参数上设置 **XMEM\_MAPPABLE**。以这种方式预留的地址空间通过 [XMemMapPhysicalPages](/zh-CN/reference/system/xmem/functions/xmemmapphysicalpages) API 进行提交。

<a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualfree">VirtualFree</a> 函数可以取消提交已提交的页，释放该页的存储；或者同时取消提交并释放已提交的页。它还可以释放已预留的页，使其成为空闲页。

除了为 XBOX 特定的内存特性提供扩展外，此 API 与 <a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc">VirtualAlloc</a> 不同之处在于不保证返回的内存已初始化为零。**XMemVirtualAlloc** 也不保证保留先前的内存内容。游戏中 **XMemVirtualAlloc** 的大多数用例涉及获取图形内存（通常从零初始化中获益不大），或者为堆实现添加内存，而堆实现从零初始化中也几乎没有获得有用价值。此外，调用方在其选择的时机自行对返回的内存进行零初始化非常简单。通过不强制执行 <a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc">VirtualAlloc</a> 的这一保证，**XMemVirtualAlloc** 的实现具有更大的灵活性，可以提供更高的性能。

**XMemVirtualAlloc** 与 <a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc">VirtualAlloc</a> 还有一个不同之处：通过此 API 调用获得的内存立即可用。相比之下，<a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc">VirtualAlloc</a> 返回的内存地址只会在第一次访问时通过昂贵的缺页操作来填充物理页。

使用 **XMemVirtualAlloc** 分配的内存应使用 <a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualfree">VirtualFree</a> 释放。

**PAGE\_NOCACHE** 和 **PAGE\_WRITECOMBINE** 页保护标志在内存预留时固定，并且不能在稍后的提交请求期间更改。在提交内存时尝试更改这些标志之一的状态将被静默忽略。内存提交将成功，但内存将保持这些标志的原始状态。

示例：

```cpp theme={null}
// Allocate standard CPU only memory, using default (4K) pages
PVOID cpuMemory = XMemVirtualAlloc (NULL,
                                    1024 * 1024,
                                    MEM_RESERVE | MEM_COMMIT,
                                    XMEM_CPU,
                                    PAGE_READWRITE);
```

```cpp theme={null}
// Allocate 1MB of tooling memory using 64K pages
PVOID cpu64kmemory = XMemVirtualAlloc (NULL,
                                       1024 * 1024,
                                       MEM_64K_PAGES | MEM_RESERVE | MEM_COMMIT,
                                       XMEM_TOOL | XMEM_CPU,
                                       PAGE_READWRITE);
```

```cpp theme={null}
// Allocate 2MB graphics memory
PVOID gfx64kmemory = XMemVirtualAlloc (NULL,
                                       2 * 1024 * 1024,
                                       MEM_2MB_PAGES | MEM_RESERVE | MEM_COMMIT,
                                       XMEM_GRAPHICS,
                                       PAGE_READWRITE | PAGE_GRAPHICS_READONLY);
```

```cpp theme={null}
// Allocate 1MB of graphics memory using 64K pages in 32b address space
PVOID gfxmemory32b = XMemVirtualAlloc (NULL,
                                       1024 * 1024,
                                       MEM_64K_PAGES | MEM_RESERVE | MEM_COMMIT,
                                       XMEM_GRAPHICS_32BIT_ADDRESS,
                                       PAGE_READONLY | PAGE_GRAPHICS_READWRITE);
```

```cpp theme={null}
// Reserve 1GB of CPU visible VA space using 64K pages
PVOID cpuReserve = XMemVirtualAlloc (NULL,
                                     1024 * 1024 * 1024,
                                     MEM_64K_PAGES | MEM_RESERVE,
                                     XMEM_CPU,
                                     PAGE_READWRITE);

// Now commit the first 2MB of the reservation
PVOID cpuCommit = XMemVirtualAlloc (cpuReserve,
                                    2 * 1024 * 1024,
                                    MEM_COMMIT,
                                    XMEM_CPU,
                                    PAGE_READWRITE);
```

```cpp theme={null}
// Reserve 4MB of visible VA space intended for direct physical page mapping
// Note that physical pages are always 64KB and this reservation assumes
// any allocations will be in 32 or 64 page (2MB\4MB) blocks
PVOID physicalReserve = XMemVirtualAlloc (NULL,
                                          4 * 1024 * 1024,
                                          MEM_2MB_PAGES | MEM_RESERVE,
                                          XMEM_GRAPHICS | XMEM_MAPPABLE,
                                          PAGE_READONLY | PAGE_GRAPHICS_COHERENT | PAGE_GRAPHICS_READWRITE);
```

## 要求

**头文件：** xmem.h

**库：** xmem.lib

**支持的平台：** XBOX One 系列主机和 XBOX Series 主机

## 另请参阅

[XMem 参考](/zh-CN/reference/system/xmem/xmem_members)<br />[XMemMapPhysicalPages](/zh-CN/reference/system/xmem/functions/xmemmapphysicalpages)<br /><a href="https://learn.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualfree">VirtualFree</a>


## Related topics

- [使用 Microsoft GDK 游戏 OS 内存管理器](/zh-CN/build/console-features/memory/system-memory-working.md)
- [XMemVirtualQuery](/zh-CN/reference/system/xmem/functions/xmemvirtualquery.md)
- [XMemMapPhysicalPages](/zh-CN/reference/system/xmem/functions/xmemmapphysicalpages.md)
- [XMemMapPhysicalPagesScatter](/zh-CN/reference/system/xmem/functions/xmemmapphysicalpagesscatter.md)
- [XMEM_OPTIONS](/zh-CN/reference/system/xmem/enums/xmem_options.md)
