4

在我的项目中,我遇到了字符串“内存不足异常”的问题,没有使用 MM。问题显示当字符串的长度达到 2 300,000 个符号时。尽管有足够的内存并且在同一部分代码中,我可以创建一个包含 100,000,000 个字符的字符串。

谷歌没有帮助,我无法反汇编它(没有技能),所以我决定创建一个最小的测试示例,我可以在少于 2 000 000 000 个符号的字符串上出现内存不足异常。我无法创建这样的示例,但我创建了一些奇怪的东西:

program Project2;
{$APPTYPE CONSOLE}
uses
   SysUtils;

var s : string;
    k : integer;

function b : string;
begin
 result := 'f';
end;

procedure c;
var ss : string;
begin
  s := s + '{' +  b + '}';
  ss :=  'a';

  if k mod 100001 = 0 then
  begin
     // ss[1] := 'd';    // uncoment me
     write(k mod 10);
  end;

  inc(k);
end;

begin
  while true do c;
end.

这段代码工作正常。它只是通过一些额外的操作向全局字符串添加一些内容。问题是,如果您取消注释标记的字符串,它会显着减慢(无论是否优化)。考虑到这个赋值在 100,001 次迭代中一次,它一定不会慢下来。

问题:

  1. Delphi 中的默认字符串是如何工作的?

  2. 如何避免减速?

  3. 如何避免内存不足?

PS 如果我将 FastMM 包含到主项目中,则错误消失 pps 未注释字符串的示例在 3 分钟内(从用户模式)将我的 Windows 7 发送到 BSOD。

4

2 回答 2

9

通过做分配字符串

s := s + '{' +  b + '}';

在长时间运行的循环中只会分散你的记忆。您可能对字符串有足够的内存,但这还不够。您需要内存是连续的,但您的分配模式会使这变得困难。

通过调用将字符串预分配到最终所需的长度来解决问题SetLength

于 2012-04-12T11:13:14.967 回答
4

1. Delphi 中的默认字符串是如何工作的?

string每次受影响时都会分配一个新的(通过 a :=)。

那是,

s := s + '{' +  b + '}';

将分配一个stringfor s + '{' + b + '}';,然后将其复制到 variable s

每次运行此行时,都会分配一次内存,释放一次内存。这可能很慢,即使使用FastMM4 也是如此。但是对于年长的MM,它可能会很

2.如何避免减速?

如果您在旧的 Delphi 中,使用“Borland”内存管理器,分配和重新分配非常慢。而且它会使内存碎片化很多。

由于内存碎片,这ss[1] := 'd'当然非常慢,而且 Borland 内存管理器必须对这一行的内存分配进行一些缓慢的清理。

使用以下命令更改行:

var ss: string[1];

而且它不会再慢下来,因为shortstring将在堆栈上分配,并且不会使用堆。

所以为了避免减速:

  • 使用现代内存管理器,例如FastMM4
  • 使用类似TStringBuilder的类,或者TMemoryStream在其中附加数据的旧类:您将拥有更少的内存重新分配,因此速度会更快

3.如何避免内存不足?

内存不足错误来自内存碎片。

所以上一个问题的两个解决方案将解决这个问题。

于 2012-04-12T13:34:05.437 回答