MakeScreenshot泄漏?

Sco*_*ard 9 delphi memory-leaks delphi-xe2 firemonkey

晚上好!

在当前的项目中,我遇到了一个相当令人担忧的内存泄漏,我似乎无法插入.

我让应用程序在标准使用情况下一夜之间运行,当我8小时后醒来时,它耗尽了大约750MB的内存,而它的起始速度却达到了50MB左右.Windows任务管理器不适合检查泄漏,除了允许您首先发现存在泄漏.

我已经清理了一些其他的内存泄漏,主要的一个与Firemonkeys有关TGlowEffect.它没有被检测到,ReportLeaksOnShutdown但是它的内存使用量在动态修改的对象上变得极其过度(例如旋转或缩放变化).

我已经将它跟踪到一个计时器(并且禁用它可以完全阻止泄漏),如果可能的话我需要帮助修复它.

说明:此代码使用Firemonkey MakeScreenshot函数将a的视觉外观保存TPanel (SigPanel)到a TMemoryStream.然后使用标准代码将此流数据上载到远程FTP服务器(见下文).在里面SigPanel,有4个TLabel孩子,1个TRectangle孩子和6个TImage孩子.

注意:CfId是一个全局字符串,它是根据随机extended浮点值生成的,随后浮点值将与格式化的DateTime一起进行哈希处理yyyymmdd_hhnnsszzz.这一代在创建表单时完成,并且一直重复直到它变为有效CfId(即不包含在Windows文件名中使用的非法字符).一旦它获得有效CfId,它就不会再次运行(因为我不再需要生成新的ID).这使我几乎可以完全消除重复的可能性CfId.

计时器中的代码如下;

var
  i : Integer;
  SigStream : TMemoryStream;
begin
  SigStream := TMemoryStream.Create;
  SigPanel.MakeScreenshot.SaveToStream(SigStream);
  SigPanel.MakeScreenshot.Free;
  if VT2SigUp.Connected then
  begin
    VT2SigUp.Put(SigStream,'Sig_'+CfId+'.png',False);
  end else
  begin
    VT2SigUp.Connect;
    VT2SigUp.Put(SigStream,'Sig_'+CfId+'.png',False);
  end;
    SigStream.Free;
end;
Run Code Online (Sandbox Code Playgroud)

与定时器NOT完全运行,则代码功能而不泄漏和ReportMemoryLeaksOnShutdownNOT生成的消息.启用定时器并允许至少"运行"一次,我得到大量泄漏,这会增加计时器运行的次数.报告的泄漏情况如下;

Small Block Leaks

1 - 12 Bytes: Unknown x 1
13 - 20 Bytes: TList x 5, Unknown x 1
21 - 28 Bytes: TFont x 2, TGradientPoint x 8, TGradientPoints x 4, Unknown x 4
29 - 36 Bytes: TObjectList<FMX.Types.TCanvasSaveState> x 1, TBrushBitmap x 4,
TBrushGrab x 4, TPosition x 24, TGradient x 4, UnicodeString x1
37 - 44 Bytes: TBrushResource x 4
53 - 60 Bytes: TBrush x 4
61 - 68 Bytes: TBitmap x 5
69 - 76 Bytes: TD2DCanvasSaveState x 1
205 - 220 Bytes: TCanvasD2D x 1

Sizes of Medium and Large Block Leaks
200236
Run Code Online (Sandbox Code Playgroud)

当计时器运行时,这些值乘以n次(n是计时器运行的次数).中型和大型块的n值为200236(例如,如果计时器已运行3次,则为200236,200236,200326).

有趣的是,如果我删除与之关联的代码MakeScreenshot,则泄漏不再存在,并且内存使用率保持在某种正常水平.除了通常的内存使用情况外,没有任何异常情况,也没有泄漏报告.我已经尝试了多个代码示例,既保存到流并从那里上传,或者保存到流>文件然后上传文件,但是在函数本身中似乎存在泄漏.我甚至MakeScreenshot.Free在这里发现泄漏时添加了,但我似乎无法插上它,当然,我已经try..finally在我的一个代码中使用了"测试运行".

我甚至用GDI +作为画布类型运行代码并且在那里发生相同的泄漏(唯一的变化是D2D泄漏引用GDI +).

我非常感谢任何人对此有任何研究或笔记,此外,还有一个问题的解决方案.

Ser*_*yuz 14

您没有释放MakeScreenshot创建的位图.

procedure TForm1.Button1Click(Sender: TObject);
var
  ms: TMemoryStream;
begin
  ms := TMemoryStream.Create;
  Panel1.MakeScreenshot.SaveToStream(ms);
  ms.Free;
end;
Run Code Online (Sandbox Code Playgroud)

上面的代码没有保留对创建的位图的引用,因此没有机会释放它.而是改变你的设计,如下所示:

procedure TForm1.Button2Click(Sender: TObject);
var
  ms: TMemoryStream;
  bmp: TBitmap;
begin
  ms := TMemoryStream.Create;
  bmp := Panel1.MakeScreenshot;
  bmp.SaveToStream(ms);
  ms.Free;
  bmp.Free;
end;
Run Code Online (Sandbox Code Playgroud)


使用下面的代码,您实际上创建了两个位图并释放其中一个位图.

  SigPanel.MakeScreenshot.SaveToStream(SigStream);
  SigPanel.MakeScreenshot.Free;
Run Code Online (Sandbox Code Playgroud)


最后,您的代码将更像下面的代码:

var
  i : Integer;
  Bmp: TBitmap;
  SigStream : TMemoryStream;
begin
  SigStream := TMemoryStream.Create;
  try
    Bmp := SigPanel.MakeScreenshot;
    try
      Bmp.SaveToStream(SigStream);
      if not VT2SigUp.Connected then
        VT2SigUp.Connect;
      VT2SigUp.Put(SigStream, 'Sig_'+CfId+'.png', False);
    finally
      Bmp.Free;
    end;
  finally
    SigStream.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

  • 补充@Sertac answear不要忘记最后使用try来避免put上的错误并避免内存流泄漏,因为如果引发任何Exception,将不会调用SigStream.Free. (2认同)
  • @Scott - 在这里发布问题或者在进行初始设计时,可以省略错误处理.当我在评论中看到它时,我只是不想遗漏它. (2认同)