编码非常大的文件时如何解决此EOutOfMemory异常?

lke*_*ler 4 delphi unicode encoding large-files

我正在使用带有Unicode字符串的Delphi 2009.

我正在尝试编码一个非常大的文件,将其转换为Unicode:

var
  Buffer: TBytes;
  Value: string;

Value := Encoding.GetString(Buffer);
Run Code Online (Sandbox Code Playgroud)

这适用于40 MB的缓冲区,其大小加倍并返回值为80 MB的Unicode字符串.

当我尝试使用300 MB缓冲区时,它会给我一个EOutOfMemory异常.

嗯,这并非完全出乎意料.但无论如何我决定追踪它.

它进入系统单元中的DynArraySetLength过程.在该过程中,它进入堆并调用ReallocMem.令我惊讶的是,它成功分配了665,124,864字节!

但是在DynArraySetLength结束时,它调用FillChar:

  // Set the new memory to all zero bits
  FillChar((PAnsiChar(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);
Run Code Online (Sandbox Code Playgroud)

你可以通过评论看到应该做什么.该例程并不多,但这是导致EOutOfMemory异常的例程.这是系统单元的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}
Run Code Online (Sandbox Code Playgroud)

所以我的记忆被分配了,但它崩溃了,试图用零填充它.这对我来说没有意义.就我而言,内存甚至不需要用零填充 - 无论如何这可能是浪费时间 - 因为编码语句无论如何都要填充它.

我可以以某种方式阻止Delphi进行内存填充吗?

或者还有其他方法可以让Delphi成功为我分配这个内存吗?

我的真正目标是为我的大文件执行Encoding语句,因此任何允许这样做的解决方案都将非常受欢迎.


结论:请参阅我对答案的评论.

这是在调试汇编代码时要小心的警告.确保你打破了所有的"RET"线,因为我错过了FillChar例程中间的那个,并错误地断定FillChar导致了这个问题.谢谢梅森,指出这一点.

我将不得不打破Chunks的输入来处理非常大的文件.

Mas*_*ler 6

FillChar没有分配任何内存,所以这不是你的问题.尝试跟踪它并在RET语句中放置断点,你会看到FillChar完成.无论问题是什么,它可能在后面的步骤中.


Rom*_*eau 5

从文件中读取一个块,编码并写入另一个文件,重复.