delphi - MakeScreenshot 泄露?

标签 delphi memory-leaks delphi-xe2 firemonkey

大家晚上好!

在当前的项目中,我遇到了相当令人担忧的内存泄漏,但我似乎无法修复它。

我让应用程序在标准使用情况下运行过夜,当我在 8 小时后醒来时,它消耗了约 750MB 内存,而它一开始的内存约为 50MB。 Windows 任务管理器不适合检查泄漏,只能让您首先发现泄漏的存在。

我已经清除了其他一些内存泄漏,其中主要的一个与 Firemonkeys 的 TGlowEffect 有关。 ReportLeaksOnShutdown 无法检测到它,但在动态修改的对象(例如旋转或缩放更改)上,它的内存使用量变得极其过多。

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

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

注释:CfId 是一个全局字符串,根据随机扩展 浮点值生成,然后与 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;

当计时器NOT运行时,代码完全可以正常运行,不会发生泄漏,并且ReportMemoryLeaksOnShutdown不会NOT生成消息。启用计时器并允许至少“运行”一次后,我会得到很多泄漏,这会增加计时器运行的次数。报告的泄漏情况如下;

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

随着计时器的运行,这些值将乘以 n 次(n 是计时器运行的次数)。中型和大型 block 的 n 值为 200236(例如,如果计时器运行了 3 次,则为 200236、200236、200326)。

有趣的是,如果我删除与 MakeScreenshot 相关的代码,泄漏将不再存在,并且内存使用量仍保持在正常水平。除了通常的内存使用情况外,没有任何异常情况,也没有报告泄漏。我已经尝试了多个代码示例,无论是保存到流并从那里上传,还是保存到流>文件然后上传文件,但函数本身似乎存在泄漏。一旦我发现这里有泄漏,我什至添加了MakeScreenshot.Free,但我似乎无法堵住它,当然,我已经使用了try..finally在我的代码之一“测试运行”中。

我什至使用 GDI+ 作为 Canvas 类型运行代码,并且那里发生了相同的泄漏(唯一的变化是 D2D 泄漏改为引用 GDI+)。

我非常感谢任何人对此进行的任何研究或注释,以及该问题的解决方案。

最佳答案

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

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

上面的代码不保留对创建的位图的引用,因此没有机会释放它。相反,请更改您的设计,如下所示:

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;


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

  SigPanel.MakeScreenshot.SaveToStream(SigStream);
  SigPanel.MakeScreenshot.Free;


最后,您的代码将类似于以下内容:

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;

关于delphi - MakeScreenshot 泄露?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10777929/

相关文章:

iphone - iOS5.0.1上的内存泄漏drawInRect

在线程内部调用时,Python 队列内存泄漏

delphi - 如何在 TIdHTTPServer 中接收包含外来字符的查询字符串

string - 鉴于 ShortString 已被弃用,限制字符串长度的最佳方法是什么

Delphi - 7 段显示器

Delphi ITask - 完成后我应该释放它吗?

RedHat Linux 中的 Java/Tomcat 内存泄漏?

delphi - 透明位图作为资源?

delphi - Delphi XE2/XE3和Windows 8平板电脑/手机

delphi - 如何将我的应用程序静音?