Memory not getting released in WPF Image - c#

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

PngBitmapEncoder failling

I'm loading bitmaps on one thread, and then later saving them to disk on another thread.
Loading on Thread A :
BitmapSource bitmapSource = null;
using (var stream = File.OpenRead(path))
{
bitmapSource = BitmapDecoder
.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
bitmapSource.Freeze();
}
// only available to Thread B at this point (i.e after loading is complete).
Saving on Thread B :
System.Diagnostics.Debug.Assert(bitmapSource.IsFrozen);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource)); <-- Exception here
...
99% of the time, this works fine. But every once in a while, I'll get an exception at the 'BitmapFrame.Create(bitmapSource)' point, with the following call stack :
at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.Media.Imaging.BitmapDecoder.get_IsDownloading()
at System.Windows.Media.Imaging.BitmapFrameDecode.get_InternalMetadata()
at System.Windows.Media.Imaging.BitmapFrame.Create(BitmapSource source)
The calling thread cannot access this object because a different thread owns it.
I'm really not sure what is going on. From my understanding, as long as you freeze the bitmapsource, you can access it from another thread? I also have a debug assert to check it is frozen, which never gets triggered. And if I check all the properties of the bitmapsource object in the debugger, I see the following :
All properties are accessable apart from the IsDownloading one. I'm not doing any downloading. I'm loading the bitmap from disk, and it's not available to the second thread until its completed loading? so it's a bit of a mystery, at least to me.
I came across this :
WPF BitmapFrame and multiple threads
The solution appears to be to wrap the bitmap in a CachedBitmap
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(new CachedBitmap(x, BitmapCreateOptions.None, BitmapCacheOption.OnLoad)));
Ideally, you could use CheckAccess() to determine it this extra step is necessary rather than always doing it, but CheckAccess() always returns true for some reason.

Why does my program's memory usage rise quickly and then go flat? [duplicate]

This question already has answers here:
What strategies and tools are useful for finding memory leaks in .NET?
(15 answers)
Understanding garbage collection in .NET
(2 answers)
Memory Leak in C#
(21 answers)
Memory Leak in C# WPF
(4 answers)
Closed 3 years ago.
Hi everyone I'm making a C# WinForms application that searches for duplicate images in a directory. It starts by calling a constructor with every image in the directory.
There is a lot of files in the directory and memory was quickly rising to 2gb and then the program would throw an out of memory exception.
Now I've added a check in my for loop to check if the memory has exceeded 800 Megabits and I force a garbage collection. But I've noticed after the first forced collection the memory no longer rises.
(The forced garbage collection occurs at loop ~180 out of ~800 then it never occurs again)
(It looks a little like a shark fin swimming through the water leaving waves in its wake.)
I'm stumped as to why this is happening and have come here in search of help.
private void GeneratePrints()
{
for (int i = 0; i < files.Count; i++)
{
if (imageFileExtensions.Contains(Path.GetExtension(files[i])))
prints.Add(new FilePrint(directory + "/" + files[i]));
//800 Megabits
const long MAX_GARBAGE = 800 * 125000;
if (GC.GetTotalMemory(false) > MAX_GARBAGE)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
Console.WriteLine("File Prints Complete.");
}
GeneratePrints() is called 1 time, once a directory is selected.
I will also show you the constructor for the FilePrint class.
I'm pretty sure this all has something to do with the MemoryStream object.
public FilePrint(string filename)
{
Bitmap img;
using (var fs = new FileStream(filename, FileMode.Open))
{
using (var ms = new MemoryStream())
{
fs.CopyTo(ms);
ms.Position = 0;
img = (Bitmap)Bitmap.FromStream(ms);
ms.Close();
}
fs.Close();
}
this.size = img.Size;
img = ResizeImage(img, 8, 8);
img = MakeGrayscale(img);
//I do some basic for-loop arithmetic here
//calculating the average color of the image, not worth posting.
img.Dispose();
}
So basically I'm wondering how can I make it so that the 'shark-fin-like' memory usage spike at the start never happens so that I do not have to force a garbage collection.
Here is a memory snapshot I have taken when the forced garbage collection occurs (Am I not disposing of the MemoryStreams properly?):
Thank you for your replies, ideas and answers in advance!
You don't show the methods ResizeImage(img, 8, 8) and MakeGrayscale(img), but most likely they simply create and return a new image based on the old. If that's true, your code constructs two Bitmap objects that it never explicitly disposes, so try disposing them e.g. as follows:
using (var old = img)
img = ResizeImage(old, 8, 8);
using (var old = img)
img = MakeGrayscale(old);
You might also want to guarantee that the final img is disposed using a try/finally:
Bitmap img = null;
try
{
img = new Bitmap(filename); // Here I simplified the code, but this will leave the file locked until `img` is disposed after resizing.
this.size = img.Size;
using (var old = img)
img = ResizeImage(old, 8, 8);
using (var old = img)
img = MakeGrayscale(old);
//I do some basic for-loop arithmetic here
//calculating the average color of the image, not worth posting.
}
finally
{
if (img != null)
img.Dispose();
}
The possible reason you get the long buildup in memory use then a precipitous drop is that eventually the unmanaged resources of the undisposed images will get finalized, however because the GC is unaware of unmanaged memory owned by the undisposed Bitmap objects, it doesn't necessarily kick in, identify the bitmaps as unreferenced and pass them on to the finalizer thread for quite a while. It's not always easy to predict when or even if the finalizer thread will spin up and start working; see Are .net finalizers always executed? to which the answer is not necessarily. But by calling GC.WaitForPendingFinalizers(); you may be kickstarting that process.
Incidentally, a MemoryStream doesn't actually need to be disposed in the current implementation as it lacks unmanaged resources. See this answer by Jon Skeet to Is a memory leak created if a MemoryStream in .NET is not closed? for confirmation. (It's still good practice to do so, though in the case of Bitmap there's that pesky file/stream lock that makes it impossible.)

Out Of Memory exception / Cleanup unmanaged memory

I'm trying to record a video from a certain window of an application (the window is showing some graphics build with DirectX) anyway
What I'm actually doing is, taking snapshots from that window and passing it to the AVIWriter from Aforge.net, I'm repeating the process 20 time per-second to get a video with 20fps.
Everything is working fine for 30 to 40 seconds, anything longer I get an out of memory exception.
Upon profiling with JetBrains dotMemory, I found that the unmanaged memory is increasing rapidly. Although the call to Dispose() is made to remove allocations. Still memory consumption is high.
I tried also using
GC.Collect();
GC.WaitForPendingFinalizers();
but without success
here my code below, it's running in a Backgroundworker which repeat every 50 ms until the button for CancellationPending is hit.
DisplayMode dm = device.DisplayMode;
Bitmap bmp = null;
Surface renderTarget = device.GetRenderTarget(0);
Surface destTarget = device.CreateOffscreenPlainSurface(ClientRectangle.Width, ClientRectangle.Height, dm.Format, Pool.SystemMemory);
device.GetRenderTargetData(renderTarget, destTarget);
GraphicsStream gs = SurfaceLoader.SaveToStream(ImageFileFormat.Bmp, destTarget);
renderTarget.Dispose();
destTarget.Dispose();
bmp = new Bitmap(gs);
gs.Dispose();
image = new Bitmap(bmp, size);
bmp.Dispose();
writer.AddFrame(image);
image.Dispose();
GC.Collect();
GC.WaitForPendingFinalizers();

Dispose and memorystream

I have the following code that gets called 4 times a second and updates the background of a grid. When i don't dispose of of the memory stream, the memory output usage slowly grows and falls. MemoryStream has a Dispose function, But if I call it even after i dispose the Source Bitmap, the background is just white.
Do i need to dispose the stream? And if I do, what am I doing wrong?
private void Viewer_OnUpdate(object self, Bitmap sourceBitmap, Int32Rect cropArea)
{
if (sourceBitmap == null)
{
return;
}
this.Dispatcher.BeginInvoke(DispatcherPriority.Render,
new Action(
() =>
{
MemoryStream stream = new MemoryStream();
sourceBitmap.Save(stream, ImageFormat.Bmp);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
stream.Seek(0, SeekOrigin.Begin);
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
this.GridContainer.Background =
new ImageBrush(new CroppedBitmap(bitmapImage,cropArea));
sourceBitmap.Dispose();
}));
}
Note: i'm dispatching because the calling event is always from a Non-UI thread
From MSDN documentation for BitmapImage (emphasis is mine):
Set the CacheOption property to BitmapCacheOption.OnLoad if you wish
to close the stream after the BitmapImage is created. The default
OnDemand cache option retains access to the stream until the bitmap is
needed, and cleanup is handled by the garbage collector.
First, you discovered that if you dispose the memory stream, the bitmap is affected, which means your ImageBrush seems white. So - don't dispose the memory stream.
Second, and more importantly, the memory consumption pattern you're seeing - more and more memory, then a sudden fall - is normal. That's what happens when the garbage collector runs at its own discretion. It's not a problem at all.
So don't dispose of the memory stream, let the garbage collector do its job, and don't worry about it.
Look, maybe this answer will help you.
MemoryStream in Using Statement - Do I need to call close()
If you put your MemoryStream in a using statement, you will not need to Dispose, because it will do it automatically ;)
I hope this helps

BitmapPalette Insufficient memory exception

I have the following piece of code that runs in a loop.
public void Test(Bitmap bmp)
{
FormatConvertedBitmap fBitmapSource = new FormatConvertedBitmap();
PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
BitmapImage bi = new BitmapImage();
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, ImageFormat.Png);
bmp.Dispose();
bmp = null;
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
BitmapPalette pallete = new BitmapPalette(bi, 256);
...
Last line
BitmapPalette pallete = new BitmapPalette(bi, 256);
Sometimes throws the following exception
Insufficient memory to continue the execution of the program.at System.Windows.Media.Imaging.BitmapPalette..ctor(BitmapSource bitmapSource, Int32 maxColorCount)
Any ideas ? I clearly have enough memory to continue execution.
There are other sources of OutOfMemoryException in a managed program that don't have anything to do with running out of managed memory. The exception is also raised when it translates error codes returned by legacy native code. Like the E_OUTOFMEMORY error that can be returned by COM method calls. And relevant in your case, by GDI+. Which has only 20 distinct error codes to indicate failure, you'll find them documented in this answer. One of them is OutOfMemory.
Which can mean more than one thing. Running out of unmanaged memory, the kind used by GDI+ to store bitmap pixels is certainly a possibility. It can also mean that your process has run out of available GDI object handles, Windows imposes a handle quota of 10,000 GDI handles. Which is an enormous number btw, exceeding that quota almost always indicates a bug in the code. A handle leak. Which in the case of a managed program is almost always caused by forgetting to use the Image.Dispose() method and not having the garbage collector run often enough to allow the finalizer to release handles.
Sadly it can even be triggered by corrupted bitmap data, not likely in your case since you bomb on allocating the palette. Which indicates a handle leak, which ought to be readily visible in Taskmgr.exe, Processes tab. View + Select columns and tick GDI Objects. Keep an eye on the displayed value for your process while you test it. A steadily increasing number spells trouble, the show is over when it reaches 10,000. Also look at the "Commit size" column, that can show you trouble with consuming too much unmanaged memory.

Categories