我尝试围绕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
为什么它在那里泄漏内存?
正如注释中提到的@kami一样,EHeapException
具有内部AllowFree
标志,默认情况下为False,以防止EHeapException
实例被异常处理程序释放。
[EOutOfResources
源自EOutOfMemory
,后者又源自EHeapException
。
SysUtils
单元具有2个类型为EOutOfMemory
和EInvalidPointer
的单例对象。每当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
,这与堆无关。
EOutOfResources
的AllowFree
标志应设置为False,而应设置为True。并且该标志为private
,因此只能由SysUtils
单元覆盖,该单元仅在完成过程中对2个单例进行覆盖。因此,基本上,[[all EHeapException
派生的异常会在运行时泄漏。
RegisterExpectedMemoryLeak()
为False时,单例以及所有其他后代实例都不会传递到RTL的AllowFree
函数,因此可以从泄漏报告中忽略它们。此泄漏问题自Delphi 5起就存在,并且已经向Embarcadero报告: