代码之家  ›  专栏  ›  技术社区  ›  Roland Bengtsson

查找最大的可用内存块

  •  3
  • Roland Bengtsson  · 技术社区  · 15 年前

    当内存碎片化时,有时会出现内存不足的问题。

    有没有可能找到最大的免费内存块? 我使用Delphi2007和Fastmm。在Windows 2003上的Windows XP运行应用程序上开发。

    当做

    编辑: 我可以添加该应用程序在WindowsServer2003x64上内存为32GB的服务器上运行的信息。但该应用程序是32位应用程序,因此每个实例的理论最大分配内存为2GB。许多实例同时运行。我不认为总的物理记忆是微不足道的。我猜这个应用程序什么时候开始有32位的虚拟内存空间。在运行时,这可能太零碎了。

    我还找到了fastgetheapstatus方法,它返回一个包含一些可用内存字段的pstatus。也许我可以用那些。

    编辑2: 我发现了这个 How to get the largest available continues memory block . 代码是C,但也许可以翻译成Delphi。

    3 回复  |  直到 15 年前
        1
  •  4
  •   mghie    15 年前

    这是您想要的Delphi代码的翻译:

    function GetLargestFreeMemRegion(var AAddressOfLargest: pointer): LongWord;
    var
      Si: TSystemInfo;
      P, dwRet: LongWord;
      Mbi: TMemoryBasicInformation;
    begin
      Result := 0;
      AAddressOfLargest := nil;
      GetSystemInfo(Si);
      P := 0;
      while P < LongWord(Si.lpMaximumApplicationAddress) do begin
        dwRet := VirtualQuery(pointer(P), Mbi, SizeOf(Mbi));
        if (dwRet > 0) and (Mbi.State and MEM_FREE <> 0) then begin
          if Result < Mbi.RegionSize then begin
            Result := Mbi.RegionSize;
            AAddressOfLargest := Mbi.BaseAddress;
          end;
          Inc(P, Mbi.RegionSize);
        end else
          Inc(P, Si.dwPageSize);
      end;
    end;
    

    您可以这样使用它:

    procedure TForm1.FormCreate(Sender: TObject);
    var
      BaseAddr: pointer;
      MemSize: LongWord;
    begin
      MemSize := GetLargestFreeMemRegion(BaseAddr);
      // allocate dynamic array of this size
      SetLength(fArrayOfBytes, MemSize - 16);
    
      Caption := Format('Largest address block: %u at %p; dynamic array at %p',
        [MemSize, BaseAddr, pointer(@fArrayOfBytes[0])]);
    end;
    

    注意,我必须从最大大小中减去16个字节,大概是因为动态数组本身使用了一些从同一块内存中分配的字节,所以下一次分配是基于16的下一个倍数。

        2
  •  6
  •   Marco van de Voort    15 年前

    不,这是老turbo pascal中的“maxavil”,这是一个经常被请求的特性,但不幸的是,在多用户、多任务环境中,它是一个无用的概念。

    堆管理器 可以 知道它自己维护的内存中最大的块,但这通常是小的,因为大的块是直接从窗口(并返回到)分配的。

    而逐步尝试分配更大块的方案将失败,因为操作系统将授予请求,即使这意味着要用磁盘交换请求(这是您不想要的)。尝试使用WindowsAPI调用挖掘此类值的技巧也是如此。

    整个保护模式环境作为内存共享的基础,每个应用程序只使用所需的资源。忽略这一点,假装一切仍然像在DOS下一样,只会引起同时运行多个应用程序的人的大量抱怨。

    如果您的应用程序真的依赖于此,请使用安全(小)默认值将其设置为配置设置(启动时为某个对象分配多少内存)。如果这真的很关键,请在安装过程中与用户面对面。

    当然,可以通过进行一些winapi调用,并假设没有其他应用程序运行,通过启发式尝试来设定默认值。但最终决定总是留给用户,特别是服务器应用程序。

        3
  •  3
  •   ConcernedOfTunbridgeWells    15 年前

    在虚拟内存系统中,虚拟地址空间意味着虚拟页可以映射到任何地方。您不需要大量连续的物理内存块。如果您的虚拟地址空间碎片化有问题,那么您可能需要一个不同的内存管理策略。

    但是,大多数选项都要求应用程序代码在某种程度上了解内存管理策略。我不认为有一个快速解决这个问题-你可能是为了合理的大手术来解决它。这些选项都不容易实现,您必须找到最有可能在特定情况下工作的选项。

    我能看到的主要选项是:自定义内存分配器、涉及AWE的内容(见下文)或在应用程序中重建内存分配策略。

    选项1:自定义内存分配器

    自定义内存分配器在C和C++循环中并不少见。您可能能够实现类似的东西。有两种可能:

    • 使用一种机制构建一个内存分配器,该机制尝试将相邻的空闲块合并到一个更大的块中(您可以将其作为尝试从失败的内存分配中恢复的一部分来运行)。这可能允许您透明地管理内存,而无需了解应用程序。实现这一点既费心又技术,但可能是可行的。

      这种方法的主要好处是它是唯一不需要您更改现有应用程序代码的方法。缺点是它不能保证工作;合并操作仍然有可能无法合并足够大的内存块来满足请求。合并操作在运行时也可能导致应用程序响应中出现明显的暂停。

    • 您可能需要以允许压缩数据结构的方式构建应用程序。这将要求您维护支持移动对象的句柄,即双间接机制。我猜可能有一种或相当少量的不同数据结构导致了这种碎片问题,所以 可以 可以在应用程序中本地化任何重新构建的工作。

    选项2:PAE

    Windows确实支持直接操作MMU的工具,并且有两种可能,这可以应用于您的应用程序。这肯定需要应用程序提供明确的体系结构支持,但提供了使用比2GB大得多的内存池的可能性。

    在服务器版本的Windows上,查找 PAE ,由支持 API's 这允许您手动操作系统的MMU并重新映射内存块。这个 可以 用两种方法之一帮助你

    • 您可以使用此机制作为管理数据的固有部分来构建数据结构的管理器。

    • 如果您可以将数据结构中的项目与页面边界相匹配,那么您可以将其用作合并内存的方法。

    但是,这种方法将要求您重新设计应用程序,以便对象引用具有足够的信息来管理显式交换进程(可能是某种覆盖管理器,具有通过此系统引用的对象的代理机制)。这意味着任何涉及PAE的解决方案都不能代替Fastmm——您必须修改应用程序以明确支持PAE。

    然而,这种代理机制可能意味着这个子系统对客户机是相对透明的。除了管理间接寻址和覆盖(这可能是一个重要问题,也可能不是一个重要问题)的开销之外,代理实际上与原始API是不可区分的。这种类型的方法最适用于具有最小互连的相对较少数量的大型、重量级对象——这种方法的规范应用程序是磁盘缓存。代理必须保持在内存中的固定位置,较大的对象通过覆盖机制进行访问。YMMV。

    选项3:修复源代码处的问题

    一种可能是可以从应用程序代码中优化对象分配策略(可能是从批量分配的对象池中进行优化,然后从应用程序中进行管理)。这可能允许您从应用程序中处理内存碎片,而不尝试重新编写内存管理器。

    同样,这种方法意味着您必须重新构建应用程序的各个部分,并且这种方法的适用性实际上取决于应用程序的性质。只有你才能判断这有多有效。