为什么我泄漏EOutOfResources中的内存?

问题描述 投票:2回答:1

我尝试围绕this jpeg解码器库(由Arnaud Bouchez原创)实现一个包装器。该库是DAMN的快速库,但不支持所有jpeg!

对于非常大的jpg文件,它会失败(按预期),并带有EOutOfResources异常。因此,我尝试以静默方式跳过这些文件。它可以工作,但是当我关闭应用程序时,FastMM指示内存泄漏。

function FastJpgDecode(FileName: string; OUT ErrorType: string): TBitmap;
var Img: PJpegDecode;
    res: TJpegDecodeError;
    Stream: TMemoryStream;
begin
  Result:= NIL;
  Stream:= TMemoryStream.Create;
  TRY
    if Length(FileName) > MAX_PATH then { TMemoryStream does not support long paths }
     begin
      ErrorType:= 'File name too long!';
      Exit;
     end;

    Stream.LoadFromFile(FileName);
    Stream.Position:= 0;
    res:= JpegDecode(Stream.Memory, Stream.Size, Img);       
    case res of
     JPEG_SUCCESS:
      begin
       try
        Result:= Img.ToBitmap; // This will raise an EOutOfResources for large files!
       except
        on EOutOfResources do
          ErrorType:= 'JPEG_OUTOFMEM!';
       end;
      end;

     JPEG_EOF                : ErrorType:= 'JPEG_EOF!';
     JPEG_OUTOFMEM           : ErrorType:= 'JPEG_OUTOFMEM!';
     JPEG_CPUNOTSUPPORTED    : ErrorType:= 'JPEG_CPUNOTSUPPORTED!';
     JPEG_BADFILE            : ErrorType:= 'JPEG_BADFILE!';
     JPEG_FORMATNOTSUPPORTED : ErrorType:= 'JPEG_FORMATNOTSUPPORTED!';       // Not all jpegs are supported. In this case we fall back to WIC or the standard LoadGraph loader (WIC).   
    end;
  FINALLY
    Img.Free;
    Stream.Free;
  END;
end;



function TJpegDecode.ToBitmap: TBitmap;
begin
  if @self=nil
  then result := nil
  else
   begin
    result := TBitmap.Create;
    try
     if not ToBitmap(result)   // This will raise an EOutOfResources for large files!
     then FreeAndNil(result);
    except
      FreeAndNil(Result);
      raise;
    end;
   end;
end;

内存块已泄漏。大小为:36

此块由线程0xD0C分配,并且堆栈跟踪(返回地址)为:407246 40830F 408ADE 43231B [未知__dbk_fcall_wrapper的函数] 407246 40A532 53C353 [未知TMethodImplementationIntercept上的函数] 6E006F [未知函数在TMethodImplementationIntercept处] 7765648F [RtlNtStatusToDosError]77656494 [RtlNtStatusToDosError] 767A7BEA [位于的未知功能IsNLSDefinedString]

该块当前用于以下类的对象:EOutOfResources分配号为:4181

从指针地址7EEEA6C0开始的256个字节的当前内存转储:74 7F ............ t D。 ü$ú......

内存块已泄漏。大小是:132该块由线程0xD0C分配,并且堆栈跟踪(返回地址)为:407246 40A2E7 40A518 53C341 [未知TMethodImplementationIntercept上的函数] 6E006F [未知函数在TMethodImplementationIntercept处] 7765648F [RtlNtStatusToDosError]77656494 [RtlNtStatusToDosError] 767A7BEA [位于的未知功能IsNLSDefinedString] 7677F0BA [VirtualQueryEx] 7677F177 [VirtualQuery]898FD9 [GetFrameBasedStackTrace]

该块当前用于类对象:UnicodeString

分配号是:4180

从指针地址7EFA24F0开始的256个字节的当前内存转储:B0 04 02 00 01 00 00 00 .........。 。 。 。 。 。 。 :。 。 。不。 。足够的存储空间。 。一世 。 s。 。一种 。 v。一种 。一世 。 l。一种 。 b。 l。 e。 。 。 。 。 。 。 。 C 。 e。 s。 s。 。 。 H 。一世 。 s。 。 C 。 。米米一种。 n。 d ............]

此应用程序已泄漏记忆。小块泄漏是(不包括预期的泄漏记录)通过指针):

21-36字节:EOutOfResources x 1117-132字节:UnicodeString x 1

为什么它在那里泄漏内存?

delphi jpeg delphi-10.3-rio
1个回答
5
投票

正如注释中提到的@kami一样,EHeapException具有内部AllowFree标志,默认情况下为False,以防止EHeapException实例被异常处理程序释放。

[EOutOfResources源自EOutOfMemory,后者又源自EHeapException

SysUtils单元具有2个类型为EOutOfMemoryEInvalidPointer的单例对象。每当RTL直接引发这两种特定的异常类型时,它每次都会引发那些类的same instance。因此,它们具有AllowFree标志,以防止异常处理程序释放单例。完成SysUtils单位后,释放单例。

这实际上是记录的行为:

http://docwiki.embarcadero.com/Libraries/en/System.SysUtils.EHeapException

注意:每当应用程序启动时,都会为这些异常预先分配内存,并且只要应用程序正在运行,它们就会一直分配。切勿直接举起EHeapException 或其后代

http://docwiki.embarcadero.com/Libraries/en/System.SysUtils.EOutOfMemory

EOutOfMemory异常的内存在应用程序启动时被预先分配,并在应用程序运行时保持分配状态。

注意:切勿直接升高EOutOfMemory。而是调用全局OutOfMemoryError过程。

但是,尽管EOutOfResources源自EHeapException,但从未以单例方式使用它,因此其AllowFree标志实际上永远不应该为False。因此在我看来,这里有几个错误:

  • EOutOfResources并不是真正的堆错误,因此不应该从EHeapException派生。实际上,这是一个常见的例外,例如,Vcl.Graphics单元针对其某些GDI错误提高了EOutOfResources,这与堆无关。

  • EOutOfResourcesAllowFree标志应设置为False,而应设置为True。并且该标志为private,因此只能由SysUtils单元覆盖,该单元仅在完成过程中对2个单例进行覆盖。因此,基本上,[[all EHeapException派生的异常会在运行时泄漏。

  • RegisterExpectedMemoryLeak()为False时,单例以及所有其他后代实例都不会传递到RTL的AllowFree函数,因此可以从泄漏报告中忽略它们。

此泄漏问题自Delphi 5起就存在,并且已经向Embarcadero报告:

RSP-17193: EOutOfResources memory leak

RSP-19737: EOutOfResources exception causes memory leak

© www.soinside.com 2019 - 2024. All rights reserved.