当Delphi中使用SetLength时发生内存不足异常时如何释放内存

6

我有一段 Delphi 代码

var
  a: array of array of array of integer;
begin
  try
   SetLength(a, 100000, 100000, 10000); // out of memory here
   doStuffs(a); 
  except
   a = nil; // try to free the memory 
  end;
end;

上面的代码尝试分配大量内存,会捕获到out-of-memory异常。虽然a=nil将被执行,但内存并没有被释放。
在内存不足异常发生的情况下,是否有一种方法可以释放内存呢?
我尝试了SetLength(a, 0, 0, 0)Finalize(a),但两者都无效。

当你在异常处理程序中时,a 是否具有非空值?如果没有,那么你几乎不能指望能够释放它。此外,它将在函数结束时被释放。你确认了 SetLength 在无法完成分配所有请求的内存时是否设置了 a 变量吗? - Rob Kennedy
@Rob 不,a 在异常处理程序中是 nil - David Heffernan
是的,当代码进入异常块时,a=nil。附注:我使用FastMM作为内存管理器。 - justyy
在这种情况下,除非程序退出,否则内存不会被释放。 - justyy
1个回答

8
一般来说,无法从内存不足错误中恢复。此时堆很可能已经损坏。适当的响应是终止进程。
在这种特定情况下,分配由“System”单元中的“DynArraySetLength”执行。这会执行重复的分配。只有在“DynArraySetLength”的最后一步才实际分配返回值,即上面代码中的“a”。如果在“DynArraySetLength”中发生错误,则运行时不会进行任何整理。这意味着在失败的情况下,分配的任何内存都会泄漏并且无法恢复。您无法引用它以便释放它。
您可能认为“DynArraySetLength”应该做更多的整理工作。然而,其方法是可以证明的。由于内存不足条件不可避免地导致堆损坏,尝试整理只会延长痛苦。一旦堆已经死亡,尝试去释放内存就没有意义了。

1
自然的后续问题是:“是否有TrySetLength过程?” - Andreas Rejbrand
1
@AndreasRejbrand 没有。 - David Heffernan
你确定吗?我认为这取决于内存管理器的内部情况,堆是否已损坏。 - kludg
@Serg 对于多维数组的SetLength情况不是这样。而且我真的认为常用的内存管理器都无法抵御OOM。 - David Heffernan
@AndreasRejbrand,你可以创建这样的函数,但是你需要编写自己的DynArraySetLength以使其更加健壮。一种更简单(但不是竞态条件证明)的方法是检查内存是否可用,然后分配它。 - Jeroen Wiert Pluimers
@DavidHeffernan 或许 Pierre le Riche 可以对OOM韧性提供一些见解。他是FastMM的作者,这是默认的Delphi内存管理器。 - Jeroen Wiert Pluimers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接