I am loading and unloading images in Canvas. I used the below code to load the Image.
Before loading my Image the memory consumption is 14.8MB.
Canvas c = new Canvas();
Image im = new Image();
ImageSource src = new BitmapImage(new Uri(#"E:Capture.png"));
im.Source = src;
im.Height = 800;
im.Width = 800;
c.Children.Add(im);
homegrid.Children.Add(c); //homegrid is my grid's name
The Image displayed correctly and the memory consumption now is 20.8MB. Then I unloaded the Image by the below code:
foreach (UIElement element in homegrid.Children)
{
if (element is Canvas)
{
Canvas page = element as Canvas;
if (page.Children.Count > 0)
{
for (int i = page.Children.Count - 1; i >= 0; i--)
{
if (page.Children[i] is Image)
(page.Children[i] as Image).Source = null;
page.Children.RemoveAt(i);
}
}
page.Children.Clear();
page = null;
}
}
homegrid.Children.RemoveAt(2);
InvalidateVisual();
The Image gets removed after this, but the memory is still 20.8 MB.
Can anyone help me out this?
First of all you should test by explicitly invoking GC.Collect() to collect memory and see that memory releases or not because GC collection is indeterministic. You can't be sure that after your method execution GC runs and reclaim the memory.
So , at end put this code to explicitly force GC to run to check if actually memory is released or not:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
However, there is some known memory leak issues in BitmapImage creation which you can refer here, here and here.
Actually under the covers WPF keeps a strong reference between the static BitmapImage and the Image and hook some events on Bitmap image. So, you should freeze the bitmapImage before assigning to image. WPF doesn't hook events on freezed bitmapImage. Also set CacheOption to avoid any caching memory leak of bitmapImage.
Image im = new Image();
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(#"E:Capture.png");
bi.EndInit();
bi.Freeze();
ImageSource src = bi;
im.Source = src;
im.Height = 800;
im.Width = 800;
In .Net there is something called the garbage collector (GC) that is in charge of managing the memory you're using.
When you create an instance of an object, it requires some more memory.
When you remove your ImageSource from the Children collection, you don't actually free any memory, you just say "I don't want to use this instance anymore".
At this point the GC will help you. It'll automatically detect instances that are not used anymore, and will free the associated memory for you.
Do note it's an automatic process and you shouldn't (and you don't want to) take care of the memory management.
You can call GC.Collect(); to force the garbage collector to do its job right now, you'll see the memory will be released. NOTE: GC.Collect(); should be used in debug to detect memory leaks, but 99% of times you shouldn't call it explicitly in production code. GC.Collect(); is an operation that can use a lot of CPU time.
Related
Notice:
Memory not getting released in WPF Image
I checked above post, in that post, the Image is associate with a BitmapImage. But for my case, I use XamlAnimatedGif package. They are different. And I verified his solution doesn't work for my issue.
Question:
In my WPF application, I use XamlAnimatedGit package to play a gif as loading animation. And after playing, I want to remove it and release its memory occupation.
My code is as below.
When app starts, I will call ShowLoading(), and after e.g. 5s, I will call HideLoading().
But if I don't add the loading gif at all, then my application will consume about 40MB memory. While by adding and playing the loading gif, my application will consume about 100MB memory, and will not release the memory even after HideLoading() is called.
LoadingBorder.Child = null; in HieLoading() causes CPU usage dropped from 2.3% to 0.1%, but it doesn't reduce the memory usage.
How to release the memory usage occupied by the gif Image?
xaml code:
<Border x:Name="LoadingBorder" Visibility="Hidden" Width="250" Height="250">
<Image Name="LoadingImageUI" Stretch="UniformToFill"/>
</Border>
cs code:
private void HideLoading()
{
if (LoadingBorder.Visibility == Visibility.Hidden)
return;
LoadingBorder.Height = 0;
LoadingBorder.Margin = new Thickness(0, 0, 0, 0);
LoadingBorder.Visibility = Visibility.Hidden;
LoadingBorder.Child = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
private void ShowLoading()
{
LoadingBorder.Height = 250;
LoadingBorder.Visibility = Visibility.Visible;
LoadingUri = new Uri("Asset/pictures/Loading_Dark.gif", UriKind.Relative);
AnimationBehavior.SetSourceUri(LoadingImageUI, LoadingUri);
}
Update:
I add GC.Collect() in HideLoading(), but the memory still cannot be released.
I have a very simple WPF app which is used to preview images in any given folder one image at a time. You can think of it as a Windows Image Viewer clone. The app has a PreviewKeyUp event used to load the previous or next image in the folder if the left arrow or right arrow key is pressed.
<Grid>
<Image x:Name="CurrentImage" />
</Grid>
private void Window_PreviewKeyUp(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Left:
DecreaseIndex();
break;
case Key.Right:
IncreaseIndex();
break;
}
var currentFile = GetCurrentFile();
CurrentImage.Source = new BitmapImage(new Uri(currentFile));
}
The problem I'm trying to solve is that there is a large amount of memory bloat when loading multiple images until garbage collection occurs. You can see this in the screenshot I took of the app's memory usage. It's not uncommon for it to exceed 300 MB before garbage collection occurs.
I tried wrapping the image in a using statement, but that doesn't work because BitmapImage doesn't implement IDisposable.
using (var image = new BitmapImage(new Uri(currentFile)))
{
CurrentImage.Source = image;
}
What can I do to prevent memory bloat when loading multiple images into my app?
When you say preview, you probably don't need the full image size. So besides of calling Freeze, you may also set the BitmapImage's DecodePixelWidth or DecodePixelHeight property.
I would also recommend to load the images directly from a FileStream instead of an Uri. Note that the online doc of the UriCachePolicy says that it is
... a value that represents the caching policy for images that come from an HTTP source.
so it might not work with local file Uris.
To be on the safe side, you could do this:
var image = new BitmapImage();
using (var stream = new FileStream(currentFile, FileMode.Open, FileAccess.Read))
{
image.BeginInit();
image.DecodePixelWidth = 100;
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
image.Freeze();
CurrentImage.Source = image;
Call .Freeze() on the bitmap object, this sets it in to a read-only state and releases some of the handles on it that prevents it from getting GC'ed.
Another thing you can do is you can tell the BitmapImage to bypass caching, the memory you see building up could be from the cache.
CurrentImage.Source = new BitmapImage(new Uri(currentFile),
new RequestCachePolicy(RequestCacheLevel.BypassCache));
Lastly, if there is not a lot of programs running on the computer putting memory pressure on the system .net is allowed to wait as long as it wants for a GC. Doing a GC is slow and lowers performance during the GC, if a GC is not necessary because no one is requesting the ram then it does not do a GC.
I'm using a RenderTargetBitmap to render a set of controls in order to generate a PDF. The following code segment is the relevant section:
public Drawing.Image RenderPageBitmap()
{
RenderTargetBitmap bit = null;
Drawing.Bitmap bmp = null;
try
{
bit = new RenderTargetBitmap(ImageSource.PixelWidth, ImageSource.PixelHeight, 96, 96, PixelFormats.Pbgra32);
var viewBox = GetPageXaml(); //This method loads some prebuilt XAML from an embedded resource, setting the DataContext as needed.
var siz = new Size(bit.PixelWidth, bit.PixelHeight);
viewBox.Measure(siz);
viewBox.Arrange(new Rect(siz));
viewBox.UpdateLayout();
var draw = new DrawingVisual();
using (var graph = draw.RenderOpen())
graph.DrawRectangle(new BitmapCacheBrush(viewBox), null, new Rect(siz));
bit.Render(draw);
bit.Freeze();
bmp = new Drawing.Bitmap(bit.PixelWidth, bit.PixelHeight, Imaging.PixelFormat.Format32bppPArgb);
var data = bmp.LockBits(new Drawing.Rectangle(Drawing.Point.Empty, bmp.Size), ImageLockMode.WriteOnly, Imaging.PixelFormat.Format32bppPArgb);
{
bit.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
}
bmp.UnlockBits(data);
return bmp;
}
catch (Exception)
{
bmp?.Dispose();
throw;
}
finally
{
bit?.Clear();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
Even following other answers on StackOverflow and other forums (like clearing the bitmap and performing a garbage collection) doesn't seem to solve the issue. Each loop of this code can leak ~100MB of memory, which means I quickly hit the ~2GB limit of 32-bit processes.
The leak seems to occur on the RenderTargetBitmap.Render method exclusively, even the DrawingContext.DrawRectangle call doesn't noticeably increase memory usage.
Is there anything I can do to solve this problem?
Here's a snapshot of the memory usage as viewed through JetBrains' dotMemory. Clearly, the .Net heap is correctly cleared, but the unmanaged memory continues to grow.
You return the Bitmap to somewhere. Make sure you Dispose the Bitmap instance once you are done with it. What you are doing in the finally is useless when there is memory leak. If there are references GC wouldn't collect it.
Each loop of this code can leak ~100MB of memory, which means I
quickly hit the ~2GB limit of 32-bit processes.
Are you assuming there is a memory leak? May be there is no memory leak. I would get a good memory profiling tool and test this.
I have used ANTS Memory profiler and I find it good (it comes with 14 days trial). Just execute your logic a few times and see the Instance List if anything is growing. If so, look at the Retention graph to see what holds onto it. That will tell you what exactly happening. Root causes for memory leaks are quite difficult to guess sometimes, fortunately there are good tools for that.
Remove bit.Freeze();. Garbage collection does not collect frozen objects.
I have an image control in my WPF application:
<Image x:Name="image" Source="{Binding}"/>
...and I'm trying to figure out which one would be the most efficient way of setting its source from an icon. I am using SystemIcons.WinLogo as my test subject.
First way involves CreateBitmpapSourceFromHIcon:
image.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
SystemIcons.WinLogo.Handle, Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
The second approach uses BitmapImage and sets its source from the memory stream:
var ms = new MemoryStream();
SystemIcons.WinLogo.ToBitmap().Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Position = 0;
var bmpi = new BitmapImage();
bmpi.BeginInit();
bmpi.StreamSource = ms;
bmpi.EndInit();
image.Source = bmpi;
Which one should I use? They both work and I haven't noticed much difference in performance on my system.
Both will serve the same purpose. If you ask me I would go with first approach because that's straight forward and no need to get icon first save in memory stream.
However, if you want to go with second approach make sure you call Freeze() on bitmapImage instance to avoid any memory leaks. Also freezing it will make it thread safe i.e. you can create a bitmapImage in background thread and can still set as Image source on UI thread.
var bmpi = new BitmapImage();
bmpi.BeginInit();
bmpi.StreamSource = ms;
bmpi.EndInit();
bmpi.Freeze(); <-- HERE
image.Source = bmpi;
Example:
var image = UIImage.FromFile("/path/to/image.png");
var imageView = new UIImageView();
imageView.Image = image;
image.Dispose();
Will the native reference from the UIImageView keep the underlying native image alive or will this crash sooner or later because the native image is actually dead?
Update:
void Foo()
{
var image = UIImage.FromFile("/path/to/image.png");
this.imageView = new UIImageView();
this.imageView.Image = image;
}
Will this leak the underlying native image?
The native UIImageView will keep a reference to the image, so it won't crash.
No, it will not leak the underlying native image. But it might end up freed a lot later than you'd expect. We recommend you call Dispose once you're done with images, since they tend to use a lot of memory which the garbage collector doesn't know about.