3

I have an application in Delphi 7, where I made the code below to load large PDF files from a blob field to memory and then I load the PDF, it works perfect with large files I have already tested with 1 gigabyte files. However, somewhere there is a memory leak and I don't know where, after loading 10 large files, it presents the Message - Out of Memory.

I am not sure how to clear the memory after loading the memory.

I already tested loading several pdf files and it works perfectly, the component has no problem. Note guys, I don't want to save it to file after loading it in the component, I want to do it directly in memory.

Note guys, I don't want to save to file on disk and then load the component, I want to do it directly in memory.

procedure TForm1.btnAbrirClick(Sender: TObject);
var
  BlobStream: TStream;
  Arquivo: Pointer;
begin
  pdf2.Active := False;
  Screen.Cursor := crHourGlass;
  try
    BlobStream := absqry1.CreateBlobStream(absqry1.FieldByName('binario'),bmRead);
    Arquivo := AllocMem(BlobStream.Size);
    BlobStream.Position := 0;
    BlobStream.ReadBuffer(Arquivo^, BlobStream.Size);
    pdf2.LoadDocument(Arquivo);
    pdfvw1.Active := True;
  finally
    Screen.Cursor := crDefault;
    BlobStream.Free;
    Arquivo := nil;
  end;
end;
Ken White
  • 123,280
  • 14
  • 225
  • 444
claudioprv
  • 199
  • 1
  • 1
  • 7

1 Answers1

6

Arquivo := nil; does not free memory allocated by AllocMem. For that, you need a call to FreeMem.

This is covered in the documentation (emphasis mine):

AllocMem allocates a memory block and initializes each byte to zero.

AllocMem allocates a block of the given Size on the heap, and returns the address of this memory. Each byte in the allocated buffer is set to zero. To dispose of the buffer, use FreeMem. If there is not enough memory available to allocate the block, an EOutOfMemory exception is raised.

I've also corrected your use of try..finally.

procedure TForm1.btnAbrirClick(Sender: TObject);
var
  BlobStream: TStream;
  Arquivo: Pointer;
begin
  pdf2.Active := False;
  Screen.Cursor := crHourGlass;
  BlobStream := absqry1.CreateBlobStream(absqry1.FieldByName('binario'),bmRead);
  try
    Arquivo := AllocMem(BlobStream.Size);
    try
      BlobStream.Position := 0;
      BlobStream.ReadBuffer(Arquivo^, BlobStream.Size);
      pdf2.LoadDocument(Arquivo);
      pdfvw1.Active := True;
    finally
      FreeMem(Arquivo);
    end;
 finally
    Screen.Cursor := crDefault;
    BlobStream.Free;
  end;
end;
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • Wouldn't be [GetMem](http://docwiki.embarcadero.com/Libraries/Sydney/en/System.GetMem) more appropriate for this scenario? – Peter Wolf Jul 27 '20 at 08:09
  • 1
    @PeterWolf GetMem() won't fill with Zeros, so may be faster for small blocks. But for huge blocks (>256KB), FastMM4 just call the Windows VirtualAlloc() API which already set with zeros. So it won't be faster in practice. – Arnaud Bouchez Jul 27 '20 at 08:58
  • I used to do it the way you taught me before, but I had problems with memory errors. I got a response from the component's author who explained that I could only clear the memory when the component is inactive. Then I changed the time to clear the memory, after pdf2.Active: = False; which is correct, now it's working 100% Thank you very much for your help and explanation. – claudioprv Jul 27 '20 at 14:09