I'm trying to figure out how to release a WriteableBitmap memory.
In the next section of code I fill the backbuffer of a WriteableBitmap with a really large amount of data from "BigImage" (3600 * 4800 px, just for testing)
If I comment the lines where bitmap and image are equaled to null, the memory it´s not release and the application consumes ~230 MB, even
when Image and bitmap are no longer used!
As you can see at the end of the code its necessary to call GC.Collect() to release the memory.
So the question is, what is the right way to free the memory used by a WriteableBitmap object? Is GC.Collect() the only way?
Any help would be great.
PS. Sorry for my bad english.
private void buttonTest_Click(object sender, RoutedEventArgs e)
{
Image image = new Image();
image.Source = new BitmapImage(new Uri("BigImage"));
WriteableBitmap bitmap = new WriteableBitmap(
(BitmapSource)image.Source);
bitmap.Lock();
// Bitmap processing
bitmap.Unlock();
image = null;
bitmap = null;
GC.Collect();
}
Are you running your test on Windows XP, using .Net 3.5 SP1?
If so, then it is a known problem, that will be fixed in 4.0.
See
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5d88cdf1-e992-4ad4-8f56-b5dbf92dcf1c
In general, memory should eventually be freed automatically as needed.
However, for that to happen, you need to be sure that the object is truly unused: No references to the object may exist anywhere, including references which "aren't used anymore". So, in particular, if you place your WriteableBitmap and original BitmapSource in variables of a long-lived class, they won't be released until the container is.
Also, WPF uses a retained GFX model: when you render, you're actually just storing instructions on how to render. The "instructions" on how to render a bitmap include a reference to the bitmap - so if you ever render a large bitmap, then for a while (at least as long as it's on screen - even if the version on screen is tiny) those images will be retained.
In practice; store references to these bitmaps only where they are needed, and if the context in which they live is long-lived (a long method call, or a method call generating a closure with a reference to the bitmaps, or a member of a long-lived class) then set them to null once they're no longer needed.
It is not necessary to manually free the memory; GC.Collect() should be superfluous. As a rule of thumb, only use GC.Collect during benchmarking to get an indication of memory consumption and/or to start with a clean slate. Overall performance is generally decreased by calls do GC.Collect().
Forcing a GC without setting image and bitmap to null won't clean them up because they are still being referenced locally and are thus considered root references. This is nothing specifically to do with the WriteableBitmap and more a question of how GC works.
If you don't set them to null and don't force a garbage collection then they will be collected once the method exists and a GC occurs. That is recommended above forcing the collection yourself because you're probably hurting performance rather than helping it.
Related
I have a simple WPF window with an Image control and a Slider control under it. When the user moves the slider, I'm loading and setting various images as Source of the Image-control. I got quite a few images, varying in size up to a 200 KB, but when I move that slider back and fourth, the program starts to eat quite a lot of memory. Hundreds and hundreds of megs of memory. Most of it gets garbage collected eventyally, but not all.
Maybe WPF isn't the way to go or should I force a G/C? I've tried loading the image as Bitmap and getting the bitmap source with Imaging.CreateBitmapSourceFromHBitmap() and Win32-api's to delete and dispose and so on, but I'm just making things worse :)
I guess I should try to grab the existing image source and release it somehow before loading and assigning a new one.
Any ideas?
EDIT
I'm adding some sample code that worked fine and seems to keep the memory low and fine:
private Image _lastImage;
// Event when user moves the slider control, load image using the filname in
// the _images[] array which contains hundreds of images
private void SliderChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (_lastImage != null)
_lastImage.Dispose();
var image = Image.FromFile(_images[(int)ImageSlider.Value]);
Snapshot.Source = ImageToBitmapImage(image);
_lastImage = image;
}
private static ImageSource ImageToBitmapImage(Image image)
{
BitmapImage bitmapImage;
using (var ms = new MemoryStream())
{
image.Save(ms, ImageFormat.Jpeg);
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(ms.ToArray());
bitmapImage.EndInit();
}
return bitmapImage;
}
Forcing a GC collection is indeed horrible. Instead call the .Dispose() method of the Bitmap object, that's what it's there for!
The method you are using (Imaging.CreateBitmapSourceFromHBitmap()) is unmanaged and probably requires that you manually .Dispose() of it.
WPF allows you to load a bitmap via a given URI : see http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.aspx
The GC will take care of collecting any managed objects you have (by using BitmapImage for example).
You are probably loading too many images to memory. the source images accumulate memory pretty fast (1024x786 image at 32bit color takes up around 3mb of ram). The GC has to work very hard to clean them up. You could reduce the size of the image after loading (scale it down) and use up less memory
Finally, the GC does not guarantee that all memory will be released after the GC cycle.
It promises "best effort". although typically calling GC.Collect(0); will force a full GC cycle and release almost everything that can be released.
From what I've seen, WPF images' source is a BitmapSource object while the Resources images are read as bitmap. I've easily found a way to convert images from Bitmap format into BitmapSource format but I believe that running this conversion every time I need an image is just costly in terms of performance and memory consumption (The Bitmaps themselves are static but I guess that the conversion allocates a new object in memory for the BitmapSource).
I thought about holding an in-memory, lazy cache for the BitmapSource objects, that way I will only need to run the conversion once per image. However, I find it hard to believe that this is the proper solution for this issue.
The nature of the application is that the data extends over time and the business objects are never disposed. Therefore, this assumption will only hold in case the implementation will hold static resources for these static images.
I will very much appreciate the proper solution for this issue.
I'm not sure I understand what you mean but you can use Image from Resources like this.
If you have an Image resource named "B1.png" and you set "Build Action" to "Resource" for it than you can use it in xaml like this.
<Image Name="c_image" Source="/YourAssemblyName;component/Resources/B1.png" />
and in code behind like this
Image img = new Image();
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri("pack://application:,,,/YourAssemblyName;component/Resources/B1.png");
bitmapImage.EndInit();
img.Source = bitmapImage;
Here's the problem :
I need to scroll a huge image of a wave form that has been rendered previously,
WPF didn't let me assign that huge (80000*256) picture to an Image,
it works for smaller (30000*256) images but it's so slow animating it that it's unpractical.
So what I decided was to use a WriteableBitmap that I assign to my Image.Source,
and then I only have to update say 1920*256 (or only the width of the screen).
It works much better now but after some profiling, it seems that
the actual copying process takes quite some time and it's not 60 fps everytime.
I am already writing to the bitmap by locking it and using pointers.
My question is :
Do you know any (faster) way to update a bitmap ?
These pixels are already in memory in the huge bitmap, reading straight from here seems a wise idea; well, get rid of the WriteableBitmap between my Image.Source and my BitmapData.
Thanks for your help :)
You can try using BitmapSource.CopyPixels to extract the bytes with color information from the image (not sure if you need that) and BitmapSource.Create that creates a bitmap from a byte array (or its overload which can grab color information directly from unmanaged memory).
You can try using InteropBitmap to update the pixels as WriteableBitmap is kind of slow with its CPU bound internal operations. I have some code here that uses it: http://silverlightviewport.codeplex.com/SourceControl/changeset/view/57274#1534506
It's hardcoded for Bgra32, which is the only color space supported by InteropBitmap in 3.5 SP1. In 4.0 it should support 24 bit color spaces.
I have fixed it, using GC.Collect; still can't believe it fixed the problem.
Don't know if it's a good practice but it works ...
That gave me headaches and nightmares the last few days but now most is sorted.
The funny conclusion I'll have about this, is that GCCollectionMode.Optimized,
thinks it's never the time to collect memory, it went up to use 2Gb,
destabilizing the whole system (Windows) up to the point it freezes the mouse, lol ...
Thanks for your help :-)
This is my first day with Silverlight. I’m trying to prototype an application which (among other functions) should be able to resize user supplied images. It should be able to handle and display several resized images at once. The most obviously approaches I've tried seem to "leak" memory in the sense that the original bitmaps are still being referenced in some way which causes Silverlight to allocate hundreds of megabytes of memory after a while. I just want to be able to load the images one by one, resize them and keep the small versions.
To be precise, I've tried the following:
Creating a list of System.Windows.Controls.Image's (and scaling them). I'm not surprised that this has not worked.
Creating a list of rectangles filled by image brushes. I'm not surprised either.
Rendering the bitmaps into System.Windows.Media.Imaging.WriteableBitmap. I expected this to perform well; I assumed that the bitmaps are really just drawn directly and not referenced in any way. However, the memory consumption indicated otherwise.
Here is a snippet of the relevant piece of code:
// create image source
Stream stream = file.OpenRead();
BitmapImage bmpImg = new BitmapImage();
bmpImg.SetSource(stream);
stream.Close();
// create temporary image from it
Image tmpImg = new Image();
tmpImg.Source = bmpImg;
// this is required by WriteableBitmap
tmpImg.Measure(new Size(100, 100));
tmpImg.Arrange(new Rect(0, 0, 100, 100));
// prepare scaling to 100x100
ScaleTransform scaleTrans = new ScaleTransform();
double scale = (double)100 / (double)Math.Max(bmpImg.PixelHeight, bmpImg.PixelWidth);
scaleTrans.CenterX = 0;
scaleTrans.CenterY = 0;
scaleTrans.ScaleX = scale;
scaleTrans.ScaleY = scale;
// render
WriteableBitmap writeableBitmap = new WriteableBitmap(100, 100);
writeableBitmap.Render(tmpImg, scaleTrans);
writeableBitmap.Invalidate();
// final image
Image img = new Image();
img.Source = writeableBitmap;
I hope I'm not missing anything silly, but it looks to OK to me and does the right thing (except the memory problem). Please also bear in mind that code quality is not supposed to be a production quality; it’s just a quick and dirty prototype.
I noticed that I’m not alone; I found questions related to image manipulation in Silverlight. I’m also aware of the fact that I could use some third party library, do processing on the server or write something even myself, but I’m surprised that Silverlight does not offer any basic image manipulation functions. It does not seem to be an uncommon requirement given where Silverlight positions itself.
Have you looked at the WriteableBitmapEx project? It's an open source project with a tonne of extension methods for the WriteableBitmap class. Here's how you resize:
BitmapImage image = new BitmapImage();
image.SetSource(dialog.File.OpenRead());
WriteableBitmap bitmap = new WriteableBitmap(image);
WriteableBitmap resizedBitmap = bitmap.Resize(500, 500, WriteableBitmapExtensions.Interpolation.Bilinear);
// For uploading
byte[] data = resizedBitmap.ToByteArray();
I don't know about the specifics here, but if you're leaking resources - you might look at which of your objects implements the IDisposable interface. I would guess that the Stream and Image classes implement this interface. And if they do - calling Dispose() on them (or wrapping their usage in a "Using" statement) will cause them to free their resources immediately rather than waiting for the garbage collector to kick in eventually.
I've created a WPF control (inheriting from FrameworkElement) that displays a tiled graphic that can be panned. Each tile is 256x256 pixels at 24bpp. I've overridden OnRender. There, I load any new tiles (as BitmapFrame), then draw all visible tiles using drawingContext.DrawImage.
Now, whenever there are more than a handful new tiles per render cycle, the framerate drops from 60fps to zero for about a second. This is not caused by loading the images (which takes in the order of milliseconds), nor by DrawImage (which takes no time at all, as it merely fills some intermediate render data structure).
My guess is that the render thread itself chokes whenever it gets a large number (~20) of new BitmapSource instances (that is, ones it had not already cached). Either it spends a lot of time converting them to some internal DirectX-compatible format or it might be a caching issue. It cannot be running out of video RAM; Perforator shows peaks at below 60MB, I have 256MB. Also, Perforator says all render targets are hardware-accelerated, so that can't be it, either.
Any insights would be appreciated!
Thanks in advance
Daniel
#RandomEngy:
BitmapScalingMode.LowQuality reduced the problem a little, but did not get rid of it. I am already loading tiles at the intended resolution. And it can't be the graphics driver, which is up-to-date (Nvidia).
I'm a little surprised to learn that scaling takes that much time. The way I understood it, a bitmap (regardless of its size) is just loaded as a Direct3D texture and then hardware-scaled. As a matter of fact, once the bitmap has been rendered for the first time, I can change its rotation and scale without any further freezes.
It's not just with a large number of images. Just one large image is enough to hold up rendering until it has been loaded in, and that can be quite noticable when your image dimensions start getting up in the thousands.
I do agree with you that it's probably the render thread: I did a test and the UI thread was still happily dispatching messages while this render delay was taking place from trying to display a fully pre-cached BitmapImage.
It must be doing some sort of conversion or preparation on the image, like you were speculating. I've tried to mitigate this in my app by "rendering" but hiding the image, then revealing it when I need to show it. However this is less than ideal because the rendering freezes happen anyway.
(Edit)
Some followup: After a discussion on the MS WPF alias I found what was causing the delays. On my Server 2008 machine it was a combination of old video drivers that don't support the new WDDM driver model and a delay for resizing the image.
If the source image size is different from the display size, that will delay the render thread before the image shows up. By default an image is set to the highest quality, but you can change the scaling options for rendering by calling RenderOptions.SetBitmapScalingMode(uiImage, BitmapScalingMode.LowQuality); . Once I did that, the mysterious freeze before displaying an image went away. An alternative, if you don't like the quality drop in scaling, is to load the BitmapImage with DecodePixelWidth/Height equal to the size it will be displayed at. Then if you load the BitmapImage on a background thread, you should have no delay in displaying it.
Also try these;
/* ivis is declared in XAML <Image x:Name="iVis" UseLayoutRounding="True" SnapsToDevicePixels="True" /> */
iVis.Stretch = Stretch.None;
RenderOptions.SetBitmapScalingMode(iVis, BitmapScalingMode.NearestNeighbor);
RenderOptions.SetEdgeMode(iVis, EdgeMode.Aliased);
VisualBitmapScalingMode = BitmapScalingMode.NearestNeighbor;
iVis.Source = **** your bitmap source ****
I was having some trouble with performance when using a huge amount of "A" channel color's, waiting until after the image had rendered to scale it worked much better for me.
Also, as you said your using a tiled graphic?
You would usually use a TileBrush to simply set as the Brush on your FrameworkElement. If you are animating them or adding new ones dynamically, you could generate your brushes then apply them to your object as you go manually too, be sure to Freeze them if you can. Also, VisualBitmapScalingMode is a property of any Visual.