【问题标题】:Strings and high memory usage字符串和高内存使用
【发布时间】:2014-01-23 14:43:24
【问题描述】:

当我在 XE4 中运行此代码时,应用程序最终使用了 ~800 MB。

为什么不接近 100 MB?

使用 Ansistring 代替字符串没有区别。

const
  N = 10000000; // 10 million
  M = 10;
var
  i,j: integer;
  s: string;
  X: array of string;
begin
  setlength(X,N);
  for i:= 1 to N do
  begin
    s:= '';
    for j:= 1 to M do s:= s+chr(65+random(25));
    X[i-1]:= s;
  end;
  showmessage('pause');
end;

【问题讨论】:

  • 我的猜测是内存碎片......但这只是一个猜测。
  • 是的,将 s:= s+ 替换为 s[j]:= 会降低到 420 MB,但仍然浪费了很多。
  • 如果你做X: Array of Array[1..10] of Char怎么办?有没有好转?
  • 大量使用字符串连接,如 s := s + ... 这更像是“堆混合器”而不是程序 ;-)
  • @UffeKousgaard 您的示例代码在您的情况下没有意义内存碎片,导致大量内存使用 主要是由于您努力填充随机字符串(通过串联,这无论如何都不是处理字符串的聪明方法)。如果您想针对内存使用优化您的代码,您首先需要制作一个小应用程序,它会生成一个包含您需要的随机内容的文件,然后将其加载到内存中进行测试。

标签: delphi


【解决方案1】:

XE4 中长度为 10 的字符串使用 34 字节的内存(请参阅DocWiki)。内容20字节,#0结束符2字节,管理数据12字节。

每个数组条目都是指向该类型内存的指针。因此,数组中的这 1000 万个字符串最终至少使用 380 MB(字符串 340 个,数组项 40 个)的内存。

【讨论】:

  • 很好的答案。如果您只是由于连续的字符串连接而添加内存碎片,则它应该是自包含的,以供以后的访问者使用。
  • @GabrielF 11 个字符不会导致明显的碎片。即使在疯狂的假设下,堆管理器会为字符串分配一个完整的兆字节,并在添加每个字符后将其移动到另一个兆字节的插槽 - 这仍然是在问题的数百 MB 中添加了一个 11 兆字节。但实际上我相信字符串 s 永远不会在内部循环中重新分配。碎片来自存储 N 个短字符串,而不是来自非常短的字符串 s 上的 M=10 连接
  • @Arioch'我不确定我理解你的意思......但内存碎片绝对是对正在使用的其他 400MiB 内存的解释。如果您检查该问题的前几个 cmets,您会看到 OP 已经确认了这一点(并且也已对此进行了解释 - 例如 KenWhite 的评论)。
  • @gabrielf 碎片来自于拥有 100m 的短字符串,但这里与串联无关。这个问题中的连接根本不会产生碎片,甚至理论上也不会在给出的代码中导致它。同样,由于碎片发生,我的代码被明确地避免它。但是您上面将碎片与字符连接循环联系起来,在这里它是完全不相关的。
【解决方案2】:

试试这个

const MaxString = 15; // you said so

type stringholder = record
  strict private 
     var Cell: string[ MaxString * SizeOf(Char) div SizeOf(AnsiChar) ];
     function GetUS: String; // in xe 4 that is a shortcut to UnicodeString actual type
     procedure SetUS(const US: string); 
  public
     property Value: string read GetUS write SetUS;
     class operator Implicit(const from: string): stringholder; inline;
     class operator Implicit(const from: stringholder): string; inline;
  end;

function stringholder.GetUS: String;
var i: integer;
begin
  i := Ord( Cell[0] );
  SetLength( Result, i div (SizeOf(Char) div SizeOf(AnsiChar)) );
  if i > 0 then
     Move( Cell[1], Result[1], i);
end;

procedure SetUS(const US: string);
var i: integer;
begin
  If US = '' then begin
     Cell := ''; // constant here, not US itself
     Exit;
  End;

  i := Length(US);
  If i > MaxString then raise EInvalidCast.Create('.....'+US);

  i := i * SizeOf(Char) div SizeOf(AnsiChar)
  Move( US[1], Cell[1], i );
  Cell(. 0 .) := AnsiChar(i);
end;

class operator stringholder.Implicit(const from: string): stringholder;
begin
  Result.Value := from;
end;

class operator stringholder.Implicit(const from: stringholder): string;
begin
  Result := from.Value;
end;


const
  N = 10000000; // 10 million
  M = 10;
var
  i,j: integer;
  s: string;
  X: array of stringholder;
begin
  setlength(X,N);
  for i:= 1 to N do
  begin
    s:= '';
    for j:= 1 to M do s:= s+chr(65+random(25));
    X(. i-1 .) := s;
  end;
  showmessage('pause');
end;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-22
    • 2011-04-30
    • 2013-05-14
    • 1970-01-01
    相关资源
    最近更新 更多