!heap -stat -h不显示分配

时间:2015-11-16 14:58:20

标签: heap windbg

我试图确定为什么我的应用程序消耗4GB的私有字节。所以我采取了一个完整的内存转储,将其加载到windbg中。但是使用!heap -stat -h进行分析会产生奇怪的结果,而这些结果并没有加起来:

0:000> !heap -s
(...)
          Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                            (k)     (k)    (k)     (k) length      blocks cont. heap 
-------------------------------------------------------------------------------------
000002d0a0000000 00000002 2800804 2780508 2800700   2984  1980   177    0      6   LFH
000002d09fe10000 00008000      64      4     64      2     1     1    0      0      
000002d09ff70000 00001002 1342924 1334876 1342820  13042  3342    87    0      0   LFH

好的,我有一个2.8GB的堆和1.34GB的堆。让我们看一下第一个的分配:

0:000> !heap -stat -h 000002d0a0000000 
 heap @ 000002d0a0000000
group-by: TOTSIZE max-display: 20
    size     #blocks     total     ( %) (percent of total busy bytes)
    651 291 - 1035e1  (16.00)
    79c 1df - e3ce4  (14.06)
    28 156d - 35908  (3.31)
    (...)

IIUC,第一行表示块大小为0x651(= 1617字节),数字块为0x291(= 657),总字节数为0x103531(= 1062369字节= ~1MB),占总数的16%忙字节。但是看一下总结,应该有~2.8GB的繁忙字节!

另一个悬殊:

0:000> !heap -stat -h 000002d0a0000000 -grp A
 heap @ 000002d0a0000000
group-by: ALLOCATIONSIZE max-display: 20
    size     #blocks    total     ( %) (percent of total busy bytes)
    a160 1 - a160  (0.62)
    7e50 2 - fca0  (0.97)

0:000> !heap -h 000002d0a0000000
(...)
(509 lines that note allocations with size 7e50, like this one:)
    000002d0a3f48000: 11560 . 07e60 [101] - busy (7e50)

编辑:许多行最后都说Internal appears to mean HEAP_ENTRY_VIRTUAL_ALLOC - 但是(7e50)的509行没有。

我的问题:如何让!heap -stat -h显示所有分配,以便它们合计为!heap -s的输出?

1 个答案:

答案 0 :(得分:3)

目前我只能解释繁忙的百分比,但这可能已经有所帮助。它的价值有点误导。

虚拟内存是从VirtualAlloc()获取的内存。 C ++堆管理器使用该基本机制从操作系统获取内存。该虚拟内存可以提交(准备使用)或保留(稍后可以提交)。 !heap -s的输出告诉您关于该虚拟内存的堆的状态。

所以我们同意C ++堆管理器可以使用的任何内存都是已提交的内存。这个粗粒度虚拟内存被C ++堆管理器拆分为更精细的块。堆管理器可以分配这些较小的块并释放它们,具体取决于malloc() / free()new / delete操作的需要。

当块变为 free 时,它们不再。同时,C ++堆管理器可能决定不将空闲块返回给OS,因为

  • 它不能,因为64k虚拟内存的其他部分仍在使用中
  • 或者它不想(内部原因,我们无法确切知道,例如表现原因)

由于 free 部分不算作 busy ,因此与虚拟内存相比,繁忙百分比似乎过高。

映射到您的案例,这意味着:

  • 你有2.8 GB的虚拟内存
  • 在堆000002d0a0000000
  • ,你有~1 MB / 16%= 6.25 MB的内存正在使用中,其余可以处于空闲堆块中(它可能不是#t; t )

以下示例基于此C ++代码:

#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <string>
#include <iomanip>

int main()
{
    HANDLE hHeap = HeapCreate(0, 0x1000000, 0x10000000); // no options, initial 16M, max 256M
    HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS, 511000); // max. allocation size for non-growing heap
    std::cout << "Debug now, handle is 0x" << std::hex << std::setfill('0') << std::setw(sizeof(HANDLE)) << hHeap << std::endl;
    std::string dummy;
    std::getline(std::cin, dummy);
    return 0;
}

唯一的511kB块将被报告为100%,尽管它只是16 MB中的1/32:

0:001> !heap -stat -h 009c0000
 heap @ 009c0000
group-by: TOTSIZE max-display: 20
    size     #blocks     total     ( %) (percent of total busy bytes)
    7cc18 1 - 7cc18  (100.00)

要查看免费部分,请使用!heap -h <heap> -f

0:001> !heap -h 0x01430000 -f
Index   Address  Name      Debugging options enabled
  3:   01430000 
    Segment at 01430000 to 11430000 (01000000 bytes committed)
    Flags:                00001000
    ForceFlags:           00000000
    Granularity:          8 bytes
    Segment Reserve:      00100000
    Segment Commit:       00002000
    DeCommit Block Thres: 00000200
    DeCommit Total Thres: 00002000
    Total Free Size:      001f05c7
    Max. Allocation Size: 7ffdefff
    Lock Variable at:     01430138
    Next TagIndex:        0000
    Maximum TagIndex:     0000
    Tag Entries:          00000000
    PsuedoTag Entries:    00000000
    Virtual Alloc List:   014300a0
    Uncommitted ranges:   01430090
    FreeList[ 00 ] at 014300c4: 01430590 . 0240e1b0  
        0240e1a8: 7cc20 . 21e38 [100] - free             <-- no. 1
        02312588: 7f000 . 7f000 [100] - free             <-- no. 2
        [...]
        01430588: 00588 . 7f000 [100] - free             <-- no. 32

    Heap entries for Segment00 in Heap 01430000
         address: psize . size  flags   state (requested size)
        01430000: 00000 . 00588 [101] - busy (587)
        01430588: 00588 . 7f000 [100]
        [...]
        02312588: 7f000 . 7f000 [100]
        02391588: 7f000 . 7cc20 [101] - busy (7cc18)
        0240e1a8: 7cc20 . 21e38 [100]
        0242ffe0: 21e38 . 00020 [111] - busy (1d)
        02430000:      0f000000      - uncommitted bytes.
0:001> ? 7cc18
Evaluate expression: 511000 = 0007cc18

这里我们看到我有一堆256 MB(240 MB未提交,0x0f000000 + 16 MB提交,0x01000000)。总结FreeList中的项目,我得到了

0:001> ? 0n31 * 7f000 + 21e38 
Evaluate expression: 16264760 = 00f82e38

所以C ++堆管理器几乎所有东西(~16 MB)都被认为是免费的而不是忙碌的。在WinDbg 6.2.9200中,!heap -s以这种方式报告了16 MB这样的内存:

0:001> !heap -s
LFH Key                   : 0x23e41d0e
Termination on corruption : ENABLED
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                    (k)     (k)    (k)     (k) length      blocks cont. heap 
-----------------------------------------------------------------------------
004d0000 00000002    1024    212   1024      6     5     1    0      0   LFH
00750000 00001002      64     20     64      9     2     1    0      0      
01430000 00001000  262144  16384 262144  15883    32     1    0      0      
    External fragmentation  96 % (32 free blocks)
-----------------------------------------------------------------------------

恕我直言有关于保留和已提交内存的错误:它应该是262144k虚拟 - 16384已提交= 245760k保留。

请注意列表长度如何与之前报告的空闲块数相匹配。

以上仅解释繁忙百分比。剩下的问题是:您的案例中报告的可用内存与此方案不匹配。

通常我会说剩下的内存是虚拟块,即大约512 kB(32位)或1 MB(64位)的内存块,如MSDN中提到的可扩展堆。但事实并非如此。

虚拟块没有输出,虚拟块数报告为0。

生成虚拟块的程序将是

#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <string>
#include <iomanip>

int main()
{
    HANDLE hHeap = HeapCreate(0, 0x1000000, 0); // no options, initial 16M, growable
    HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS, 20*1024*1024); // 20 MB, force growing
    std::cout << "Debug now, handle is 0x" << std::hex << std::setfill('0') << std::setw(sizeof(HANDLE)) << hHeap << std::endl;
    std::string dummy;
    std::getline(std::cin, dummy);
    return 0;
}

并且!heap命令会提到虚拟块:

0:001> !heap -s
LFH Key                   : 0x7140028b
Termination on corruption : ENABLED
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                    (k)     (k)    (k)     (k) length      blocks cont. heap 
-----------------------------------------------------------------------------
006d0000 00000002    1024    212   1024      6     5     1    0      0   LFH
001d0000 00001002      64     20     64      9     2     1    0      0      
Virtual block: 01810000 - 01810000 (size 00000000)
00810000 00001002   16384  16384  16384  16382    33     1    1      0      
    External fragmentation  99 % (33 free blocks)
-----------------------------------------------------------------------------

然而,在你的情况下,虚拟块的值是0.也许这被报告为&#34;内部&#34;在您的WinDbg版本中。如果您尚未升级,请尝试使用版本6.2.9200以获得与我相同的输出。