我正在使用带有Unicode字符串的Delphi 2009。
我试图对一个非常大的文件进行编码以将其转换为Unicode:
var
Buffer: TBytes;
Value: string;
Value := Encoding.GetString(Buffer);
这个方法对于一个大小为40MB的缓冲区运行良好,当缓冲区大小加倍时,返回一个80MB的Unicode字符串。
但是当我尝试使用300MB的缓冲区时,会出现EOutOfMemory异常。
这不是完全出乎意料的。但是我还是决定跟踪一下它。 它进入了System单元中的DynArraySetLength过程。 在该过程中,它访问堆并调用ReallocMem。 令我惊讶的是,它成功地分配了665,124,864字节!!!
但是在DynArraySetLength的末尾,它调用了FillChar:
// Set the new memory to all zero bits
FillChar((PAnsiChar(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);
您可以从注释中看出它的作用。这个例程并不复杂,但它会引发EOutOfMemory异常。以下是System单元中的FillChar函数:
procedure _FillChar(var Dest; count: Integer; Value: Char);
{$IFDEF PUREPASCAL}
var
I: Integer;
P: PAnsiChar;
begin
P := PAnsiChar(@Dest);
for I := count-1 downto 0 do
P[I] := Value;
end;
{$ELSE}
asm // Size = 153 Bytes
CMP EDX, 32
MOV CH, CL // Copy Value into both Bytes of CX
JL @@Small
MOV [EAX ], CX // Fill First 8 Bytes
MOV [EAX+2], CX
MOV [EAX+4], CX
MOV [EAX+6], CX
SUB EDX, 16
FLD QWORD PTR [EAX]
FST QWORD PTR [EAX+EDX] // Fill Last 16 Bytes
FST QWORD PTR [EAX+EDX+8]
MOV ECX, EAX
AND ECX, 7 // 8-Byte Align Writes
SUB ECX, 8
SUB EAX, ECX
ADD EDX, ECX
ADD EAX, EDX
NEG EDX
@@Loop:
FST QWORD PTR [EAX+EDX] // Fill 16 Bytes per Loop
FST QWORD PTR [EAX+EDX+8]
ADD EDX, 16
JL @@Loop
FFREE ST(0)
FINCSTP
RET
NOP
NOP
NOP
@@Small:
TEST EDX, EDX
JLE @@Done
MOV [EAX+EDX-1], CL // Fill Last Byte
AND EDX, -2 // No. of Words to Fill
NEG EDX
LEA EDX, [@@SmallFill + 60 + EDX * 2]
JMP EDX
NOP // Align Jump Destinations
NOP
@@SmallFill:
MOV [EAX+28], CX
MOV [EAX+26], CX
MOV [EAX+24], CX
MOV [EAX+22], CX
MOV [EAX+20], CX
MOV [EAX+18], CX
MOV [EAX+16], CX
MOV [EAX+14], CX
MOV [EAX+12], CX
MOV [EAX+10], CX
MOV [EAX+ 8], CX
MOV [EAX+ 6], CX
MOV [EAX+ 4], CX
MOV [EAX+ 2], CX
MOV [EAX ], CX
RET // DO NOT REMOVE - This is for Alignment
@@Done:
end;
{$ENDIF}
我的内存已经被分配了,但是试图用零填充它时崩溃了。这对我来说没有意义。就我所知,内存甚至不需要被填充为零 - 这可能还浪费时间 - 因为编码语句即将填充它。
我能否以某种方式防止Delphi进行内存填充?
或者是否有其他方法可以使Delphi成功地为我分配此内存?
我的真正目标是对非常大的文件执行编码语句,因此任何可实现此目标的解决方案都将不胜感激。
结论:请参见答案中的评论。
这是一个警告,要小心调试汇编代码。确保在所有“RET”行上中断,因为我错过了FillChar例程中间的一个"RET"行,错误地得出了FillChar引起了问题的结论。谢谢Mason指出这一点。
我将不得不将输入分成块以处理非常大的文件。