Korzystam z poniższego kodu, aby utworzyć kafelek na żywo, oparty na elemencie interfejsu użytkownika. Powoduje, że uiElement
na WriteableBitmap
, zapisuje bitmap + zwraca nazwę pliku. Ta metoda jest uruchamiana w agencie zadań działających w tle Windows i uruchamiana jest limit pamięci.WriteableBitmap Memory Leak?
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;
}
Zrobiłem kilka testów i problem jest: Ten metody przecieki pamięci, ale nie wiem dlaczego /gdzie ?!
Zrobiłem też kilka przebiegów testowych - przed pierwszym napotkasz tej metody:
Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage
7.249.920 Bytes
To jest ok, ponieważ debugger jest dołączony, który wykorzystuje około 2 MB pamięci.
robi jakieś kolejne serie tej metody (cofnięty ponownie uruchomić metodę debugger):
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
więc pamięć, wykorzystywane przez Metoda ta wzrasta.
Ale dlaczego? Moim zdaniem nie ma żadnych odniesień, które uniemożliwiałyby zbieranie przedmiotów.
UPDATE na 04.05.2013
Cześć,
dziękuję za wszystkie odpowiedzi! Zgodnie z sugestią, zmniejszyłem kod + ostatecznie udało mi się odtworzyć problem w kilku liniach kodu.
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;
}
Po uruchomieniu aplikacji: ApplicationCurrentMemoryUsage: "11 681 792 bajty"
1 iteracji - ApplicationCurrentMemoryUsage: "28 090 368 bajtów"
5 iteracji - ApplicationCurrentMemoryUsage: "77 111 296 bajtów"
20 powtórzeń - ApplicationCurrentMemoryUsage: "260 378 624 bajtów"
Po 23 iteracjach: Wyjątek pamięci. Ln .: var writeableBitmap = new WriteableBitmap (rectangle, rectangle.RenderTransform);
Tylko komentując linię "Fill = solidColorBrush", metoda CreateImage() została wywołana 100 razy bez żadnych problemów - po 100. iteracji zużycie pamięci wynosiło "16 064 512 Bytes".
Wydaje się, że problemem jest pędzel !! Podczas wypełniania elementu interfejsu użytkownika, a później ten element interfejsu użytkownika jest renderowany na zapisywalnej mapie bitowej, bitmapa nigdy nie zbiera śmieci.
Oczywiście to nie ma sensu w mojej opinii. Pędzel rozchodzi się poza zasięgiem, więc również powinien być zbierany śmieci! (Ustawianie szczotki na null po tym jak został użyty nic nie zmieni)
Wielu moich elementów interfejsu użyć szczotki do napełniania, więc nie można po prostu usunąć zużycia szczotek. Co sądzisz o tym problemie?
Należy zauważyć, że zazwyczaj nie ma powodu, aby wywoływać bezpośrednio GC.Collect(). Może to być szkodliwe, zamrozić aplikację i uzyskać wydajność. –
To prawda, to było coś w rodzaju "desperackiej próby", w rzeczywistości nic nie robi, a także nie ma wpływu na zużycie pamięci. – Hannes
Ciekawi Cię, co stanie się, jeśli sfinalizujesz płótno? – Tebc