内存碎片时有时会出现内存不足的问题。
是否有可能找到最大的空闲内存块? 我使用Delphi 2007和FastMM。在Windows 2003上运行应用程序的Windows XP上进行开发。
此致
修改 我可以在Windows Server 2003 x64上添加应用程序在具有32 GB内存的服务器上运行的信息。但该应用程序是一个32位应用程序,因此每个实例的理论最大分配内存为2 GB。许多实例立即运行。我认为总体物理记忆不是很少。我想在启动时应用程序获得了32位虚拟内存空间。这可能在运行时过于分散。
我还发现了FastGetHeapStatus方法,该方法返回一个THeapStatus,其中包含一些可用内存字段。也许我可以使用它们。
EDIT2: 我找到了How to get the largest available continues memory block。 代码是C但也许可以翻译成Delphi。
答案 0 :(得分:6)
不,这是旧Turbo Pascal中的“maxavail”,这是一个经常被要求的功能,但不幸的是,它在多用户,多任务环境中是一个无用的概念
heapmanager 可以知道它自己维护的内存中最大的块,但这通常很小,因为大块直接从(并返回)窗口分配。
逐步尝试分配更大块的方案将失败,因为操作系统将授予请求,即使这意味着交换到磁盘(您不想要)。尝试使用windows api调用挖掘此类值的技巧也是如此。
整个保护模式环境的基础是共享内存,每个应用程序仅使用所需的内容。忽略这一点并假装一切仍然像在Dos下只会产生大量投诉,这些人会立即投入多个应用程序。
如果您的应用程序确实依赖于此,请使用安全(小)默认值将其设置为配置设置(在启动时分配多少内存)。如果真的很重要,请在设置期间与用户对话
当然,人们可以通过做一些winapi调用并假设没有其他应用程序运行来启发尝试来启动默认值。但是,请始终将最终决定留给用户,特别是服务器应用程序。
答案 1 :(得分: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的下一个倍数。 / p>
答案 2 :(得分:3)
在虚拟内存系统上,虚拟地址空间意味着虚拟页面可以映射到任何位置。您不需要大量连续的物理内存块。如果您遇到虚拟地址空间碎片问题,则可能需要不同的内存管理策略。
但是,大多数选项都要求您的应用程序代码在某种程度上了解内存管理策略。我不相信有一个快速解决这个问题的方法 - 你可能需要通过合理的大手术来解决这个问题。这些选项都不易实现,您必须找到最适合您特定情况的选项。
我可以看到的主要选项是:自定义内存分配器,涉及AWE(见下文)或在应用程序内重建内存分配策略。
选项1:自定义内存分配器
自定义内存分配器在C和C ++圈子中并不罕见。您可能能够实现类似的东西。有两种可能性:
构建一个内存分配器,其机制尝试将相邻的空闲块合并为一个更大的块(您可以将其作为尝试从失败的内存分配中恢复的一部分来运行)。这可能允许您透明地管理内存,而无需知道应用程序。实现这一点将是繁琐和技术性的,但可能是可行的。
这种方法的主要好处是它是唯一不需要您更改现有应用程序代码的方法。缺点是它不能保证工作;合并操作仍然可能无法合并足够大的内存块以满足请求。合并操作还可能导致应用程序响应在运行时出现大量暂停。
您可能需要以允许压缩数据结构的方式构建应用程序。这将要求您维护支持被移动对象的句柄,即双间接机制。我猜测可能有一个或相当少的不同数据结构导致这个碎片问题,因此可能可以本地化应用程序中的任何重新架构工作。
选项2:PAE
Windows确实支持直接操作MMU的工具,并且有几种可能性适用于您的应用程序。这肯定需要您的应用程序提供显式的架构支持,但可以使用大于2GB的内存池。
在Windows的服务器版本上,查看PAE支持的API's,它允许您手动操作系统的MMU并重新映射内存块。 可能以两种方式之一对您有所帮助
您可以使用此机制作为管理数据的固有部分,为数据结构构建管理器。
如果您可以将数据结构中的项目放到页面边界,则可以将其用作整合内存的方法。
但是,这种方法需要您重新设计应用程序,以便对象引用有足够的信息来管理显式交换过程(可能是某种带有代理机制的覆盖管理器,用于通过此系统引用的对象) )。这意味着任何涉及PAE的解决方案都不是FastMM的直接替代品 - 您必须修改应用程序以明确支持PAE。
但是,这种代理机制可能意味着该子系统对客户端可能相对透明。除了管理间接和覆盖的开销(可能是也可能不是重要问题)之外,代理几乎与原始API无法区分。这种方法最适用于相对较少数量的大型重量级对象,互连最少 - 规范应用就是磁盘缓存。代理必须保留在内存中的固定位置,通过覆盖机制访问较大的对象。 YMMV。
选项3:在源头修复问题
一种可能性是您的对象分配策略可以在应用程序代码中进行优化(可能来自批量分配的对象池,然后从应用程序内进行管理)。这可能允许您在应用程序内处理内存碎片而无需重新编写内存管理器。
同样,这种方法意味着您将不得不重新构建应用程序的一部分,并且该方法的适用性实际上取决于应用程序的性质。只有你可以判断这可能有多好。