2010-02-15 96 views
3

当内存碎片化时,内存耗尽有时会出现问题。查找最大的空闲内存块

是否有可能找到最大的空闲内存块? 我使用Delphi 2007与FastMM。在Windows 2003上开发Windows XP上运行的应用程序

问候

编辑: 我可以添加该应用程序与基于Windows Server 2003的64位32 GB内存的服务器上运行的信息。但该应用程序是32位应用程序,因此每个实例的理论最大分配内存为2 GB。许多实例一次运行。我认为这并不是总的物理内存。我猜应用程序启动时有32位虚拟内存空间。这在运行时可能会过于分散。

我还发现方法FastGetHeapStatus返回一个THeapStatus与一些字段的空闲内存。也许我可以使用这些。

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

+2

小改正:在64位操作系统版本上,为32位进程获得完整的4 GB地址范围。该进程可能无法使用它(由于在内存管理例程中使用了签名数据类型),但限制与在32位操作系统上不同。 AFAIR Delphi *确实有问题。 – mghie

+0

是的,delphi在RTL中使用了很多有符号值的值 –

回答

4

这是翻译,以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的下一个倍数。

6

不,这是在旧的Turbo Pascal,一个经常被要求特征中的“maxavail”,但不幸的是它是在一个多用户无用的概念,多任务环境

的heapmanager 可以知道的最大块在内存中它保持自己,但通常很小,因为大块直接从窗口分配(并返回)。

而逐步尝试分配更大块的方案将失败,因为操作系统将会授予请求,即使这意味着交换到磁盘(您不需要)。同样的技巧,试图通过Windows API调用挖掘这些值。

整个保护模式环境的基本原理是共享内存,每个应用程序只使用尽可能多的内存。忽略这一点,假装一切仍然像在DOS下一样,只会产生一次同时运行多个应用程序的人的大量抱怨。

如果您的应用程序真的依赖于此,请使用安全(小)默认值将其设置为配置设置(启动时分配多少内存)。如果它是非常重要的,在安装过程中用户与它对抗

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

3

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

但是,大多数选项都需要您的应用程序代码在某个级别上了解内存管理策略。我不相信这个问题有一个快速的解决方法 - 你可能需要进行合理的大手术来解决这个问题。这些选项都不易实现,您将不得不找到最适合您的特定情况的选项。

我可以看到的主要选项是:自定义内存分配器,涉及AWE的内容(请参见下文)或重新构建应用程序内的内存分配策略。

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

定制存储器分配器不在C和C++界少见。你可能能够实现类似的东西。两种可能性都向你敞开:

  • 构建了一个机制内存分配试图相邻空闲块合并成一个更大的块(可以为试图从失败的内存分配中恢复的一部分运行此)。这可能允许您透明地管理内存,而无需应用程序注意。实现这一点将是狡猾和技术性的,但可能是可行的。

    这种方法的主要优点是它是唯一不需要您更改现有应用程序代码的方法。缺点是不能保证工作;合并操作仍有可能无法合并足够大的内存块来完成请求。合并操作在运行时也可能会在应用程序响应中造成重大暂停。

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

选项2:PAE

Windows不支持设施直接操作MMU,有几个可能的地方,这可能适用于您的应用程序。这绝对需要您的应用程序提供明确的体系结构支持,但它提供了使用比2GB大得多的内存池的可能性。

在Windows的服务器版本上,查看API's支持的PAE,它允许您手动操作系统的MMU并重新映射内存块。在两种方式之一

  • 你可以建立数据结构中的经理在使用这种机制作为管理数据的固有部分,某种程度上,这可能对您有所帮助。

  • 如果您可以使数据结构中的项目适合页面边界,则可以将其用作巩固内存的一种方法。

然而,这种方法需要您重新设计应用程序,以便对象的引用有足够的信息来管理显性换入过程中(可能是某类覆盖管理与一个代理机构的对象是通过这个系统引用)。这意味着涉及PAE的任何解决方案都不是FastMM的直接替代方案 - 您必须修改应用程序以明确支持PAE。

但是,这种代理机制可能意味着此子系统对客户端可能相对透明。除了管理间接和覆盖(这可能是也可能不是重要问题)的开销之外,代理可能实际上与原始API无法区分。这种类型的方法对于相对较少数量的大型重量级对象来说效果最佳,并且互连最少 - 标准应用程序是磁盘缓存。代理将不得不保持在内存中的固定位置,通过覆盖机制访问更大的对象。因人而异。

方案3:在源

一种可能性是你的对象分配策略可以从应用程序代码内优化(可能是从散装分配对象的池修复问题,然后从应用程序内管理)。这可能允许您在应用程序中处理内存碎片,而不尝试重写内存管理器。

此方法意味着您将不得不重新构建应用程序的某些部分,并且该方法的适用性实际上取决于应用程序的性质。只有你可以成为判断这可能工作得如何。

+0

1)如果块是不可移动的,合并相邻块并不会产生太大的效果。一个自己的memmanager可能是一个绝望的解决方案,但你可能必须增加关于应用程序访问模式的知识给经理,使其明显优于fastmm。 2)PAE没有帮助,因为Delphi编译器是32位,而不是64位,并且采用32位的lineair指针。不存储低4位的方案会破坏指针运算。将内存密集比特分解为64位FPC dll是比较理想的解决方案。我想结合分配启动时需要的最大块 –

+0

选项3,感谢您的答案。我们没有建立一个自己的记忆管理者,我相信这一点。我们真正希望的是64位的Delphi编译器,因为今天的内存很便宜。在此期间,我在应用程序中添加了一个记忆表,以便用户观察情况。在EOutOfMemory的情况下,我建议重新启动... :) –