20

以下のコードを使用して、UI要素に基づいてライブタイルを作成しています。をレンダリングしuiElementWriteableBitmapビットマップを保存し、ファイル名を返します。このメソッドは、WindowsPhoneのバックグラウンドタスクエージェントで実行されます。メモリ制限が発生しています。

private string CreateLiveTileImage(Canvas uiElement, int width, int heigth)
{
   var wbmp = new WriteableBitmap(width, heigth);
   try
   {
      wbmp.Render(uiElement, null);
      wbmp.Invalidate();

      var tileImageName = _liveTileStoreLocation;
      using (var stream = new IsolatedStorageFileStream(tileImageName, FileMode.Create, FileAccess.Write, IsolatedStorageFile.GetUserStoreForApplication()))
      {
         wbmp.SaveJpeg(stream, width, heigth, 0, 100);
         stream.Close();
      }

      uiElement = null;
      wbmp = null;
      GC.Collect();
      return "isostore:" + tileImageName;
   }
   catch (Exception exception)
   {
      // ...
   }
   return null;
}

私はいくつかのテストを行いましたが、問題は次のとおりです。このメソッドはメモリをリークしますが、理由/場所がわかりません。

また、このメソッドを最初に実行する前に、いくつかのテストを実行しました。

Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
7.249.920 Bytes

デバッガーが接続されているため、これは問題ありません。デバッガーは約2MBのメモリを使用します。

このメソッドをさらに実行する(デバッガーでメソッドを再度実行するように設定し直す):

Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
8851456  long +  40960
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
8892416  long + 245760
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9138176  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9281536  long + 151552
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9433088  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9576448  long + 139264
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9715712  long + 139264
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
9859072  long + 143360
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
10006528 long + 147456

したがって、このメソッドで使用されるメモリが増加します。

しかし、なぜ?私の意見では、オブジェクトの収集を妨げる参照はありません。

2013年5月4日の更新

やあ、

すべての回答ありがとうございます!提案されたように、私はコードを減らしました+最終的に数行のコードで問題を再現することができました。

void Main()
{
   for (int i = 0; i < 100; i++)
   {
      CreateImage();
   }
}

private void CreateImage()
{
   var rectangle = CreateRectangle();
   var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);

   rectangle = null;
   writeableBitmap = null;
   GC.Collect();
}

private Rectangle CreateRectangle()
{
   var solidColorBrush = new SolidColorBrush(Colors.Blue);
   var rectangle = new Rectangle
   {
   Width = 1000,
   Height = 1000,
   Fill = solidColorBrush  // !!! THIS causes that the image writeableBitmap never gets garbage collected
   };

   return rectangle;
}

アプリを起動した後:ApplicationCurrentMemoryUsage: "11681792バイト"

1回の反復-ApplicationCurrentMemoryUsage: "28090368バイト"

5回の反復-ApplicationCurrentMemoryUsage: "77 111 296 Bytes"

20回の反復-ApplicationCurrentMemoryUsage: "260 378 624 Bytes"

23回の反復後:メモリ不足の例外。 Ln .: var writeableBitmap = new WriteableBitmap(rectangle、rectangle.RenderTransform);

「Fill=solidColorBrush」の行をコメントアウトするだけで、CreateImage()メソッドは問題なく100回呼び出されました。100回目の反復後、メモリ使用量は約「16064512バイト」でした。

だから問題はブラシだと思います!! UI要素を埋めるために使用され、後でこのUI要素が書き込み可能なビットマップにレンダリングされると、ビットマップがガベージコレクションされることはありません。

もちろん、これは私の意見では意味がありません。ブラシが範囲外になるので、ガベージコレクションも行う必要があります。(使用後にブラシをnullに設定しても、何も変更されませんでした)

私のUI要素の多くは塗りつぶしにブラシを使用しているため、ブラシの使用を単純に削除することはできません。この問題についてどう思いますか?

4

5 に答える 5

10

問題は、rectangle.RenderTransformがオブジェクトのインスタンスであり、writableBitmapをnullに設定した場合、rectangle.RenderTransformオブジェクトはまだ存続しており、メモリ内に長方形を保持しているため、解決策は次のようにコードを編集することです。

private void CreateImage()
{
   var rectangle = CreateRectangle();
   var writeableBitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);

   rectangle.RenderTransform = null; //and your memory would be happy ;)
   rectangle = null;
   writeableBitmap = null;
   GC.Collect();
}

メモリのスクリーンショットを参照してください...

前:

rectangle.RenderTransformをnullに設定せずにメモリ

後:

rectangle.RenderTransformをnullに設定したメモリ

于 2013-05-30T13:53:11.123 に答える
0

最後にトライキャッチでキックオフするにはwbmp=nullである必要があります。

そして、それをレンダリングします。つまり、そのオブジェクトを外部(関数)リストにアタッチします。したがって、GC.Collectはまだ「使用中」であるため、実際に収集することはありません。

uielementソースをnullに設定します。これにより、GCがそれを無視する理由に添付された参照が削除される可能性があります。

于 2013-02-20T20:45:00.007 に答える
0

サイズを大きくするとメモリが増えるため、書き込み可能なビットマップ画像の幅Xの高さのサイズを小さいサイズに制限できるため、最初に初期サイズを制限してから、元のタイルサイズでjpegとして保存できます。

于 2013-02-21T10:59:45.310 に答える
0

taskagentでuielementをwriteablebitmapに変換すると、同じ例外が発生します。何度も試してみました。解決策がうまくいくことがわかりました。

wbmp.SaveJpeg(stream, width, heigth, 0, 60);

uielementをストリームに保存するときに、品質を60に変更できます。メモリ使用量を大幅に削減します。あなたはそれを試すことができます。

于 2013-12-13T04:47:03.523 に答える
-1

この部分を入れてみてください:

      uiElement = null;
      wbmp = null;
      GC.Collect();

最後に節に;

于 2013-02-24T02:20:17.897 に答える