单个VirtualAlloc分配使用的内存(和其他资源)

时间:2010-08-09 11:16:06

标签: c++ c winapi memory-management

个人VirtualAlloc (xxxx, yyy, MEM_RESERVE, zzz)使用了多少内存或其他资源?

当我分配一个大块时,资源消耗是否有任何差异(例如内核分页/非分页池),如下所示:

VirtualAlloc( xxxx, 1024*1024, MEM_RESERVE, PAGE_READWRITE )

或多个较小的块,如下所示:

VirtualAlloc( xxxx, 64*1024, MEM_RESERVE, PAGE_READWRITE );
VirtualAlloc( xxxx+1*64*1024, 64*1024, MEM_RESERVE, PAGE_READWRITE );
VirtualAlloc( xxxx+2*64*1024, 64*1024, MEM_RESERVE, PAGE_READWRITE );
...
VirtualAlloc( xxxx+15*64*1024, 64*1024, MEM_RESERVE, PAGE_READWRITE );

如果有人不知道答案,但可以提出一个可以检查的实验,那么它也会有所帮助。

我的目标是在Windows下为TCMalloc实现返回内存的操作系统。我的想法是通过执行一系列小的(分配粒度)调用来替换单个大型VirtualAlloc调用,这样我就可以在每个调用上调用VirtualFree。我知道这种方式大块的分配会比较慢,但预计会有任何资源消耗处罚吗?

4 个答案:

答案 0 :(得分:3)

仅供参考,您可以使用GetProcessMemoryInfo和GlobalMemoryStatusEx来获取一些内存使用量。

void DisplayMemoryUsageInformation()
{
    HANDLE hProcess = GetCurrentProcess();
    PROCESS_MEMORY_COUNTERS pmc;
    ZeroMemory(&pmc,sizeof(pmc));
    GetProcessMemoryInfo(hProcess,&pmc, sizeof(pmc));
    std::cout << "PageFaultCount:             " << pmc.PageFaultCount             << std::endl;
    std::cout << "PeakWorkingSetSize:         " << pmc.PeakWorkingSetSize         << std::endl;
    std::cout << "WorkingSetSize:             " << pmc.WorkingSetSize             << std::endl;
    std::cout << "QuotaPeakPagedPoolUsage:    " << pmc.QuotaPeakPagedPoolUsage    << std::endl;
    std::cout << "QuotaPagedPoolUsage:        " << pmc.QuotaPagedPoolUsage        << std::endl;
    std::cout << "QuotaPeakNonPagedPoolUsage: " << pmc.QuotaPeakNonPagedPoolUsage << std::endl;
    std::cout << "QuotaNonPagedPoolUsage:     " << pmc.QuotaNonPagedPoolUsage     << std::endl;
    std::cout << "PagefileUsage:              " << pmc.PagefileUsage              << std::endl;
    std::cout << "PeakPagefileUsage:          " << pmc.PeakPagefileUsage          << std::endl;

    MEMORYSTATUSEX msx;
    ZeroMemory(&msx,sizeof(msx));
    msx.dwLength = sizeof(msx);
    GlobalMemoryStatusEx(&msx);
    std::cout << "MemoryLoad:                 " << msx.dwMemoryLoad               << std::endl;
    std::cout << "TotalPhys:                  " << msx.ullTotalPhys               << std::endl;
    std::cout << "AvailPhys:                  " << msx.ullAvailPhys               << std::endl;
    std::cout << "TotalPageFile:              " << msx.ullTotalPageFile           << std::endl;
    std::cout << "AvailPageFile:              " << msx.ullAvailPageFile           << std::endl;
    std::cout << "TotalVirtual:               " << msx.ullTotalVirtual            << std::endl;
    std::cout << "AvailVirtual:               " << msx.ullAvailVirtual            << std::endl;
    std::cout << "AvailExtendedVirtual:       " << msx.ullAvailExtendedVirtual    << std::endl;
}

答案 1 :(得分:2)

通过使用reserve param进行VirtualAlloc调用来使用零或几乎为零的内存。这将只保留进程中的地址空间。在您使用提交参数使用VirtualAlloc实际使用页面返回地址之前,将不会使用该内存。 这实际上是虚拟字节,所占用的地址空间量和专用字节之间的差异,即提交的内存量。 您对VirtualAlloc()的两种使用都将保留相同的内存量,因此它们在资源消耗方面是等效的。 我建议你在决定编写自己的分配器之前先做一些阅读。其中一个最好的来源是Mark Russinivich。你应该检查一下his blog。他撰写了一些名为“推动极限”的条目,其中包括一些内容。如果你想了解真正的细节,那么你应该阅读他的书(Microsoft Windows Internals)。这是迄今为止我读过的关于Windows如何管理内存(以及其他所有内容)的最佳参考。

(编辑)附加信息: 相关部分是“页面目录”和“页面表”。根据我的旧版Microsoft Windows Internals ...在x86上,每个进程只有一个页面目录,有1024个条目。最多有512个页面表。在此过程中使用的每个32位指针分为3个[31-22]页目录索引,[21-12]是页表索引,[11-0]是页面中的字节索引。 将虚拟alloc与预留参数一起使用时,将创建页目录条目(32位),并创建32位页表条目。此时,不会为保留的内存创建页面。 查看此信息的最佳方法是使用内核调试程序。我建议使用LiveKD(sysinternals)。您可以在不连接远程计算机的情况下使用liveKD,但它不允许实时调试。加载LiveKD,然后选择您的流程。然后,您可以运行!PTE命令来检查该进程的页表。

同样,我建议阅读Inside Windows Internals。在我的版本(第4版)中,有一章(超过100页)涵盖所有这些,以及遍历liveKD中各种数据结构的示例。

答案 2 :(得分:0)

根据我对页面表的理解,您可以使用例如1024页,每页一个字。无论如何,这是成本的页数,而不是分配。但是,可能有其他机制每次分配花费“额外”(我只是不知道)。

仍然:使用VirtualFree,您可以有选择地退出单个页面或页面范围。对于已停用的页面,虚拟地址范围(在您的进程中)仍然保留,但没有为其分配物理内存(RAM或交换文件)。您可以稍后使用VirtualAlloc再次提交这些页面。

因此,除非您需要为进程中的其他分配器释放地址空间,否则可以使用此机制有选择地请求并将内存返回给操作系统。

[edit]

<强>测量
对于测量,我虽然在一个或多个典型负载(人工/随机分配模式,分配密集的“真实世界”应用等)下比较两种算法的性能。优点:你得到了“整个故事” - 内核资源,页面碎片,应用程序性能等。缺点:你必须实现这两种算法,你不知道原因,你可能需要非常特殊的情况才能实现可测量的差异从噪音中消失。

地址空间碎片警告 - 请小心返回算法。当以“任何人是免费的”方式将单个页面返回到进程时,您最终可能会得到一个碎片化的地址空间,该空间具有80%的可用内存,但连续没有100K。

答案 3 :(得分:0)

您可以尝试使用“perfmon”并添加计数器(例如Memory)以开始了解VirtualAlloc正在使用的资源。您必须在调用VirtualAlloc

之前和之后拍摄快照

另一种选择可能是调试进程,在WinDBG下调用VirtualAlloc并使用与内存相关的命令http://windbg.info/doc/1-common-cmds.html#20_memory_heap来了解实际发生的情况。