代码之家  ›  专栏  ›  技术社区  ›  sharptooth

如何在我的C++程序中检测和估计堆碎片?

  •  8
  • sharptooth  · 技术社区  · 15 年前

    我正在开发一个VC++的NT服务,目的是连续运行数月。它集中使用VC++运行时堆。显然,堆碎片可能在某个时候导致它出现故障(认为它内存不足)。

    我可以在我的服务上运行哪些测试来估计它容易出现堆碎片的程度?

    6 回复  |  直到 15 年前
        1
  •  6
  •   Jerry Coffin    15 年前

    您已经得到了一些关于如何防止堆碎片问题的答案,但都没有直接解决您的问题。估计碎片问题发生的可能性的唯一方法是模拟大量使用,并测量得到的碎片。

    由于它是一个NT服务,模拟几个月的使用主要是匆忙地提出很多请求。很可能您可以比通常预期的接收请求的速度更快地发出请求,因此您可能只需几个小时就可以模拟几个月的请求,甚至可能更少(取决于您通常预期接收请求的速度)。

    一旦你模拟了几个月的工作(甚至是你正在做的工作),你就需要看看堆,看看你得到了多少碎片。这不容易,但通常是可能的。您将首先向服务进程中注入一个线程(在“线程注入”或该订单上的某些内容上搜索应该会得到相当多的信息)。然后,您将需要遍历堆,寻找(特别是)空闲的、但太小而不可能满足大多数请求的块。假设你使用的是VC++,你用堆HEAPHACKE遍历堆,它会遍历堆,告诉堆中每个块的地址、大小和状态(免费或正在使用)。

    最后一个细节:为了产生有意义的结果,可执行文件和包含注入线程的DLL都必须链接到DLL中的运行时库。这意味着整个过程将有一个堆,因此注入的线程将遍历服务使用的堆。如果静态链接标准库,则DLL和服务都将有自己的堆。DLL将遍历它自己的堆,这不会告诉您有关服务进程正在使用的堆的任何信息。

        2
  •  2
  •   Tobias Langner    15 年前

    我想最好的方法是编写自己的内存管理器(或者购买一个)来提供这些数据。任何其他方法都会改变堆本身,从而使结果无效。

    一个更容易实现的策略是分配不同大小的内存块并等待失败-但我认为这不是一个好方法。不管怎样-块大小越大,没有失败,碎片就越少。但是,根据内存管理器的不同,分配块可以更改结果。


    编辑:我找到了一个关于slab分配器的链接(注释是thx),显示了统计信息。不过,这篇文章是用德语写的,英文版没有那么多信息。用巴贝菲什来翻译。

    http://de.wikipedia.org/wiki/Slab_allocator ( babelfish version )

    http://www.usenix.org/event/usenix01/full_papers/bonwick/bonwick.pdf

        3
  •  2
  •   Totonga    15 年前

    在新系统上,其默认打开状态(Vista,Server 2008)

      HANDLE heaps[1025];
      DWORD nheaps = GetProcessHeaps((sizeof(heaps) / sizeof(HANDLE)) - 1, heaps);
      for (DWORD i = 0; i < nheaps; ++i) {
        ULONG  enableLFH = 2;
        HeapSetInformation(heaps[i], HeapCompatibilityInformation, &enableLFH, sizeof(enableLFH));
      }
    

    VMMap 来自sysinternals(现在是Microsoft),它很好地概述了内存碎片。

        4
  •  1
  •   chizel    12 年前

    检测碎片的最简单方法是确定程序将进行的最大分配,然后时不时地分配至少两倍于此的数量。如果分配失败,即返回空值,堆的使用量由代码决定-类似于Windows上的

    PROCESS_MEMORY_COUNTERS counters;
    if(GetProcessMemoryInfo(process, &counters, sizeof(counters))){
        result = counters.WorkingSetSize;
    }   
    

    小于系统内存的某个百分比(通常为75%),则肯定存在碎片问题。

        5
  •  0
  •   Tim    15 年前

    另一种可能性是,时不时地对对象进行某种垃圾收集/合并-在低负载下。。。i、 e.你的服务可以在一段时间内处于非活动状态,同时它“整理”它所使用的内存,但我不确定你是否能保证在没有自己的内存管理的情况下实现你想要的行为。

        6
  •  0
  •   Barak C    15 年前

    首先,您应该了解您执行的分配是什么。我认为简单的方法是重写new和delete运算符,从这些新运算符中,您应该计算一些分配的统计信息,然后调用编译器的默认new和delete运算符。

    在我看来,您应该计算的最小统计数据是公共块大小范围的分配数。

    例如

    还可以添加每个块大小范围的顺序分配数

    收集此数据后,可以通过以下方法减少碎片问题 .

    最佳且简单的对齐技术是使用2次方的块。

    例如,要将数字与除以16的最接近数字对齐,可以使用以下函数:

    int align(int size)
    {
        return ((size + 15) & ~0x0000000F);
    }
    

    当然,你应该使用你的统计数据来选择2的最佳幂。 目标是达到一个数字,您的大多数分配将进入几个块范围,同时保持合理的对齐开销。