10

こんばんは!

現在のプロジェクトでは、プラグインできないように見えるかなり心配なメモリ リークが発生しています。

私は標準的な使用法でアプリケーションを一晩実行したままにし、8時間後に目が覚めたとき、それは〜50MBで始まったのに〜750MBのメモリを消費していました. Windows タスク マネージャーは、リークが存在することを最初に確認できるようにする以外に、リークのチェックには適していません。

その他のいくつかのメモリ リークは既に解決しましたが、主なものは Firemonkeys に関連するものでしTGlowEffectた。検出されませんReportLeaksOnShutdownが、動的に変更されたオブジェクト (回転やスケールの変更など) でメモリ使用量が非常に過剰になります。

私はそれをタイマーまで追跡しました(そしてそれを無効にするとリークが完全に止まります)、可能であればそれを修正するための支援が必要です.

説明:このコードは、FiremonkeyMakeScreenshot関数を使用して の外観を に保存しTPanel (SigPanel)ますTMemoryStream。このストリーム データは、標準コードを使用してリモート FTP サーバーにアップロードされます (以下を参照)。中SigPanelには4TLabel人、1人TRectangle、6TImage人の子供がいます。

:CfIdはグローバル文字列であり、ランダムな float 値に基づいて生成され、extendedその後 format の 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;

タイマーが実行されていない場合、コードはリークなしで完全に機能し、メッセージを生成ReportMemoryLeaksOnShutdownしません。タイマーを有効にして、少なくとも 1 回は「実行」できるようにすると、タイマーの実行回数が増えるほど多くのリークが発生します。報告されたリークは次のとおりです。

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 のn 個の値があります (たとえば、タイマーが 3 回実行された場合、それは 200236、200236、200326 です)。

興味深いことに、に関連付けられているコードを削除するとMakeScreenshot、リークはなくなり、メモリ使用量はやや正常なレベルのままです。通常のメモリ使用量以外に異常はなく、リークも報告されていません。ストリームに保存してそこからアップロードするか、ストリーム > ファイルに保存してからファイルをアップロードすることで、複数のコード サンプルを試しましたが、関数自体にリークがあるようです。ここでリークを発見したら追加しましMakeScreenshot.Freeたが、それを差し込むことができないようです。もちろん、try..finallyコードの「テスト実行」の1つで使用しました。

キャンバス タイプとして GDI+ を使用してコードを実行したところ、同じリークが発生しました (唯一の変更点は、D2D リークが代わりに GDI+ を参照することです)。

これに関する研究やメモ、さらにはこの問題の解決策を教えていただければ幸いです。

4

2 に答える 2

15

作成するビットマップを解放していません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;


以下のコードでは、実際には 2 つのビットマップを作成し、そのうちの 1 つを解放しています。

  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;
于 2012-05-28T00:23:51.037 に答える