delphi – MakeScreenshot泄漏?

前端之家收集整理的这篇文章主要介绍了delphi – MakeScreenshot泄漏?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
晚上好啊

在目前的项目中,我正在经历一个令人担忧的内存泄漏,我似乎无法插拔.

我离开应用程序运行一夜,标准用途,当我8点钟醒来,它吃了〜750MB内存,而开始在〜50MB. Windows任务管理器不适合检查泄漏,除了允许您首先找出存在的泄漏.

我已经清除了一些其他内存泄漏,主要与Firemonkeys的TGlowEffect有关. ReportLeaksOnShutdown不会检测到它,但是动态修改的对象(例如旋转或缩放更改)的内存使用情况变得非常大.

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

说明:此代码使用Firemonkey MakeScreenshot功能将TPanel(SigPanel)的视觉外观保存到TMemoryStream.然后使用标准代码将此流数据上传到远程FTP服务器(见下文). SigPanel内有4名TLabel儿童,1名TRectangle儿童和6名TImage儿童.

注意:CfId是一个全局字符串,并且是基于一个随机扩展的浮点值生成的,然后与格式为yyyymmdd_hhnnsszzz的DateTime一起散列.这一代是在创建表单时完成的,它会重复,直到它成为一个有效的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,False);
  end;
    SigStream.Free;
end;

定时器不运行,代码完全没有泄漏,ReportMemoryLeaksOnShutdown不生成消息.启用定时器并允许“运行”至少一次,我会收到大量的泄漏,这增加了定时器运行的次数.报告的泄漏如下:

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是定时器运行的次数).中型和大型块的价值为200236(例如,如果计时器已经运行3次,则是200236,200236,200326).

感兴趣的是,如果我删除与MakeScreenshot相关联的代码,则泄漏不再存在,并且内存使用率保持在某种程度上.除了通常的内存使用情况,没有什么是普通的,没有泄漏报告.我已经尝试了多个代码示例,无论是保存到流,还是从那里上传,还是保存到流>文件然后上传文件,但在函数本身似乎有泄漏.我甚至添加了MakeScreenshot.Free一旦我发现一个漏洞在这里,但我根本看不到插件,当然,我已经使用try..finally在我的一个代码“测试运行”.

我甚至使用GDI运行代码作为画布类型,同样的泄漏发生在那里(唯一的变化是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,False);
    finally
      Bmp.Free;
    end;
  finally
    SigStream.Free;
  end;
end;

猜你在找的Delphi相关文章