Delphi 为什么我要在资源中泄漏内存?
我尝试在jpeg解码器库(Arnaud Bouchez原创)周围实现一个包装器。这个库非常快,但它不支持所有的JPEG 对于非常大的jpg文件,它会失败(如预期的那样),并出现资源异常。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
所以我试着悄悄地跳过那些文件。它可以工作,但当我关闭应用程序时,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
。这是“正常”的,因为当“正常”内存管理失败时会引发此异常