Delphi 为什么我要在资源中泄漏内存?

Delphi 为什么我要在资源中泄漏内存?,delphi,jpeg,delphi-10.3-rio,Delphi,Jpeg,Delphi 10.3 Rio,我尝试在jpeg解码器库(Arnaud Bouchez原创)周围实现一个包装器。这个库非常快,但它不支持所有的JPEG 对于非常大的jpg文件,它会失败(如预期的那样),并出现资源异常。 所以我试着悄悄地跳过那些文件。它可以工作,但当我关闭应用程序时,FastMM表示内存泄漏 function FastJpgDecode(FileName: string; OUT ErrorType: string): TBitmap; var Img: PJpegDecode; res: TJpegD

我尝试在jpeg解码器库(Arnaud Bouchez原创)周围实现一个包装器。这个库非常快,但它不支持所有的JPEG

对于非常大的jpg文件,它会失败(如预期的那样),并出现资源异常。
所以我试着悄悄地跳过那些文件。它可以工作,但当我关闭应用程序时,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 408331B[未知 函数位于_dbk_fcall_wrapper]407246 40A532 53C353[未知 TMethodImplementationIntercept]6E006F处的函数[未知函数 在TMethodImplementationIntercept]7765648F[RTLNTSTATUSTODROR] 77656494[RtlNtStatusToDosError]767A7BEA[位于的未知函数 IsNLSDefinedString]

该块当前用于类为:资源的对象 分配号码是:4181

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

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

该块当前用于类为Unicode Destring的对象

分配号码是:4180

从指针地址7EFA24F0开始的256字节的当前内存转储: B0 04 02 00 01 00 00 00。。。。。。。。。 . . . . . . . : . . . 不足够的存储空间。我sA.vA.我LA.B . LETo。PR哦。CEs . sTH我sC哦。MMA. . Nd

此应用程序已泄漏 记忆。小砌块泄漏为(不包括已登记的预期泄漏 按指针):

21-36字节:1×1 117-132字节:正在分解x 1


为什么会泄漏内存?

正如@kami在评论中提到的,
EHeapException
有一个默认为False的内部
AllowFree
标志,防止异常处理程序释放
EHeapException
的实例

EOutOfResources
派生自
EOutOfMemory
,后者又派生自
EHeapException

SysUtils
单元有两个类型为
eoutomemory
EInvalidPointer
的单例对象。每当RTL直接引发这两种特定的异常类型时,它每次都会引发这些类的同一个实例。因此,它们有一个
AllowFree
标志来防止异常处理程序释放单例。当
SysUtils
单元完成时,将释放单例

这实际上是记录在案的行为:

注意:每当应用程序启动时,这些异常的内存都是预先分配的,只要应用程序正在运行,内存就一直保持分配状态。切勿直接引发
EHeapException
或其后代

每当应用程序启动时,
EOutOfMemory
异常的内存都会预先分配,并且只要应用程序正在运行,内存就会一直保持分配状态

注意:切勿直接将
提升到内存中。相反,调用全局
OutOfMemoryError
过程

然而,尽管
EOutOfResources
源于
EHeapException
,但它从未以单例方式使用过,因此它的
AllowFree
标志实际上不应该为False。因此,在我看来,这里有几个bug在起作用:

  • EOutOfResources
    实际上不是堆错误,并且不应该从
    EHeapException
    中派生出来。这实际上是一个常见的异常,例如,
    Vcl.Graphics
    单元为其一些GDI错误引发
    EOutOfResources
    ,这些错误与堆无关

  • EOutOfResources
    将其
    AllowFree
    标志设置为False,而该标志应为True。并且该标志是
    private
    ,因此它不能被覆盖,除非被
    SysUtils
    单元覆盖,该单元在完成过程中仅对2个单例进行覆盖。因此,基本上,所有
    EHeapException
    派生的异常都会在运行时泄漏

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

该泄漏问题自Delphi 5以来就存在,并已向Embarcadero报告:


看起来有点像异常/错误返回的数据,最终变成孤立的。
EOutOfMemory
-非常特殊的异常。它包含私有标志
AllowFree
(默认为False)。此标志阻止创建的异常正常释放。请参见
EHeapException.FreeInstance
。这是“正常”的,因为当“正常”内存管理失败时会引发此异常