Although I have been programming for about 11 years(mostly VB6, last 6 months C#), it's THE first time to actually ask a question :) I have found all my answers from teh interwebz but this issue i can't solve myself. Your site is among the most helpful places i have got the best answers from!
I will show the code i'm using (an extract of what's relevant). Problem is that when using RotateFlip method then the memory is increasing rapidly to ~200M and then get's collected by GC after some time. The main method calling it iterates about 30 times per second so the performance is of utmost importance here. I have tried using graphics matrix transform but this sometimes fails and shows non-flipped image. The application itself is based on using a webcam, hiding the preview, taking the callback picture and showing it in picturebox. It then overlays a rectangle on if from another class. That's the reason to use callback and not preview window.
Capture.cs class:
internal Bitmap LiveImage;
int ISampleGrabberCB.BufferCB(double bufferSize, IntPtr pBuffer, int bufferLen)
{
LiveImage = new Bitmap(_width, _height, _stride, PixelFormat.Format24bppRgb, pBuffer);
if (ExpImg) // local bool, used rarely when the picture saving is triggered
{
LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
var a = LiveImage.Clone(new Rectangle(Currect.Left, Currect.Top, Currect.Width, Currect.Height),
LiveImage.PixelFormat);
using (a)
a.Save("ocr.bmp", ImageFormat.Bmp);
}
else // dmnit, rotateflip leaks like h*ll but matrix transform doesn't sometimes flip :S
{
LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
/*using (var g = Graphics.FromImage(LiveImage))
{
g.Transform = _mtx;
g.DrawImage(LiveImage, 0, 0);
}*/
}
GC.Collect(); // gotta use it with rotateflip, otherwise it gets crazy big, like ~200M :O
return 0;
}
}
In main form i have an event that's updating the picture in the picturebox:
private void SetPic()
{
pctCamera.Image = _cam.LiveImage;
_cam.PicIsFree = false;
}
Because i need to get the image to main form which is in another class then i figured the most logical is the exposed Bitmap which is updated on every callback frame.
The reason i don't want to use matrix transform is because it's slower and sometimes with this speed it fails to flip the image and the frequency of such behavior is quite different with different PC's with different hardware capabilities and CPU speeds, also the fastest framerate 30fps with a 1.2GHz CPU shows this very frequently.
So, can you help me to figure it out? I'm not actually using it in current version, i'm using the commented-out matrix transform because i feel bad for using GC.Collect :(
Thank You!!!
pctCamera.Image = _cam.LiveImage;
Heavy memory usage like you observe is a sure sign that you missed an opportunity to call Dispose() somewhere, letting the unmanaged resources (memory mostly) used by a bitmap get released early instead of letting the garbage collector do it. The quoted statement is one such case, you are not disposing the old image referenced by the picture box. Fix:
if (pctCamera.Image != null) pctCamera.Image.Dispose();
pctCamera.Image = _cam.LiveImage;
You can rewrite your code like this:
internal Bitmap LiveImage;
int ISampleGrabberCB.BufferCB(double bufferSize, IntPtr pBuffer, int bufferLen)
{
using (LiveImage = new Bitmap(_width, _height, _stride, PixelFormat.Format24bppRgb, pBuffer))
{
LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
if (ExpImg) // local bool, used rarely when the picture saving is triggered
{
var a = LiveImage.Clone(new Rectangle(Currect.Left, Currect.Top, Currect.Width, Currect.Height),
LiveImage.PixelFormat);
using (a)
a.Save("ocr.bmp", ImageFormat.Bmp);
}
}
return 0;
}
Bitmap is an Image class, and implements the IDispose. As you create Bitmap each time, I suggest to use using statement for automatically freeing the resources.
GC.Collect is there for this situation. Collecting the data is the ONLY way to free it and when creating HUGE bitmaps its the way to go. Does a GC.Collect really slow things down?
Other then that you should keep the number of bitmap copies as low as possible.
Related
This is the problem:
I save a Bitmap in .png with colors say ARGB(50,210,102,70) with dimension 1 x 1 pixel.
I retrieve the same image again and use the GetPixel(0,0) method, what I get is ARGB(50,209,102,70).
There is a slight variation in the retrieved value, the RGB values slightly differ but the A value remains same.
However when i use 255 for A value, the correct RGB values are returned.
So,.. Using a value less than 255 for A results in the problem mentioned above.
Here is the code which saves the Bitmap.
Bitmap bmpPut = new Bitmap(1, 1); //Also tried with 'PixelFormat.Format32bppArgb'
bmpPut.SetPixel(0, 0, Color.FromArgb(254, 220, 210, 70));
bmpPut.Save("1.png"); //Also tried with using 'ImageFormat.Png'
Here is the code which gets the pixel color
Bitmap bit = new Bitmap(Image.FromFile("1.png"));
MessageBox.Show("R:" + bit.GetPixel(0, 0).R.ToString() +
"| G: " + bit.GetPixel(0, 0).G.ToString() +
"| B: " + bit.GetPixel(0, 0).B.ToString() +
"| A: " + bit.GetPixel(0, 0).A.ToString());
What i get is ARGB(254,219,209,70)
P.S.: I read a few similar questions, they were'nt addressing this exact issue and I din't find a solution yet.
mammago has found a workaround, namely using the class constructor to construct a Bitmap object directly from a file, rather than constructing a Bitmap object indirectly via the Image object returned by Image.FromFile().
The purpose of this answer is to explain why that works, and in particular, what the actual difference is between the two approaches that causes different per-pixel color values to be obtained.
One proposal for the difference was color management. However, this appears to be a non-starter, as neither invocation is asking for color-management (ICM) support.
You can, however, tell a lot by inspecting the source code for the .NET BCL. In a comment, mammago posted links to the code for the Image and Bitmap class implementations, but wasn't able to discern the relevant differences.
Let's start with the Bitmap class constructor that creates a Bitmap object directly from a file, since that's the simplest:
public Bitmap(String filename) {
IntSecurity.DemandReadFileIO(filename);
// GDI+ will read this file multiple times. Get the fully qualified path
// so if our app changes default directory we won't get an error
filename = Path.GetFullPath(filename);
IntPtr bitmap = IntPtr.Zero;
int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(filename, out bitmap);
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
status = SafeNativeMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap));
if (status != SafeNativeMethods.Gdip.Ok) {
SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(null, bitmap));
throw SafeNativeMethods.Gdip.StatusException(status);
}
SetNativeImage(bitmap);
EnsureSave(this, filename, null);
}
Lots of stuff going on there, but most of it is not relevant. The first bits of code simply obtain and validate the path. After that is the important bit: a call to the native GDI+ function, GdipCreateBitmapFromFile, one of the many Bitmap-related functions provided by the GDI+ flat API. It does exactly what you would think, it creates a Bitmap object from a path to an image file without using color matching (ICM). This is the function that does the heavy lifting. The .NET wrapper then checks for errors and validates the resulting object again. If validation fails, it cleans up and throws an exception. If validation succeeds, it saves the handle in a member variable (the call to SetNativeImage), and then calls a function (EnsureSave) that does nothing unless the image if a GIF. Since this one isn't, we'll ignore that completely.
Okay, so conceptually, this is just a big, expensive wrapper around GdipCreateBitmapFromFile that performs a bunch of redundant validation.
What about Image.FromFile()? Well, the overload you're actually calling is just a stub that forwards to the other overload, passing false to indicate that color matching (ICM) is not desired. The code for the interesting overload is as follows:
public static Image FromFile(String filename,
bool useEmbeddedColorManagement) {
if (!File.Exists(filename)) {
IntSecurity.DemandReadFileIO(filename);
throw new FileNotFoundException(filename);
}
// GDI+ will read this file multiple times. Get the fully qualified path
// so if our app changes default directory we won't get an error
filename = Path.GetFullPath(filename);
IntPtr image = IntPtr.Zero;
int status;
if (useEmbeddedColorManagement) {
status = SafeNativeMethods.Gdip.GdipLoadImageFromFileICM(filename, out image);
}
else {
status = SafeNativeMethods.Gdip.GdipLoadImageFromFile(filename, out image);
}
if (status != SafeNativeMethods.Gdip.Ok)
throw SafeNativeMethods.Gdip.StatusException(status);
status = SafeNativeMethods.Gdip.GdipImageForceValidation(new HandleRef(null, image));
if (status != SafeNativeMethods.Gdip.Ok) {
SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(null, image));
throw SafeNativeMethods.Gdip.StatusException(status);
}
Image img = CreateImageObject(image);
EnsureSave(img, filename, null);
return img;
}
This looks very much the same. It validates the file name in a slightly different way, but that isn't failing here, so we can ignore these differences. If embedded color management was not requested, it delegates to another native GDI+ flat API function to do the heavy lifting: GdipLoadImageFromFile.
Others have speculated that the difference may be a result of these two different native functions. It's a good theory, but I disassembled these functions, and though they have distinct implementations, there are no salient differences that would account for the behavior observed here. GdipCreateBitmapFromFile will perform validation, attempt to load a metafile if possible, and then call down to the constructor for an internal GpBitmap class to do the actual loading. GdipLoadImageFromFile is implemented similarly, except that it arrives at the GpBitmap class constructor indirectly via the internal GpImage::LoadImage function. Furthermore, I was unable to reproduce the behavior you described by calling these native functions directly in C++, so that eliminates them as candidates for an explanation.
Interestingly, I was also unable to reproduce the behavior you describe by casting the result of Image.FromFile to a Bitmap, e.g.:
Bitmap bit = (Bitmap)(Image.FromFile("1.png"));
Although not a good idea to rely on it, you can see that this is actually legal if you go back to the source code for Image.FromFile. It calls the internal CreateImageObject function, which delegates either to Bitmap.FromGDIplus to Metafile.FromGDIplus according to the actual type of the image being loaded. The Bitmap.FromGDIplus function just constructs a Bitmap object, calls the SetNativeImage function we have already seen to set its underlying handle, and returns that Bitmap object. Therefore, when you load a bitmap image from a file, Image.FromFile actually returns a Bitmap object. And this Bitmap object behaves identically to one created using the Bitmap class constructor.
The key to reproducing the behavior is to create a new Bitmap object based on the result of Image.FromFile, which is what exactly your original code did:
Bitmap bit = new Bitmap(Image.FromFile("1.png"));
This will call the Bitmap class constructor that takes an Image object, which delegates internally to one that takes explicit dimensions:
public Bitmap(Image original, int width, int height) : this(width, height) {
Graphics g = null;
try {
g = Graphics.FromImage(this);
g.Clear(Color.Transparent);
g.DrawImage(original, 0, 0, width, height);
}
finally {
if (g != null) {
g.Dispose();
}
}
}
And here is where we finally find an explanation for the behavior you describe in the question! You can see that it creates a temporary Graphics object from the specified Image object, fills the Graphics object with a transparent color, and finally draws a copy of the specified Image into that Graphics context. At this point, it is not the same image you're working with, but a copy of that image. This is where color matching can kick in, as well as a variety of other things that potentially affect the image.
In fact, aside from the unexpected behavior described in the question, the code you had written hid a bug: it fails to dispose the temporary Image object created by Image.FromFile!
Mystery solved. Apologies for the long, indirect answer, but hopefully it has taught you something about debugging! Do continue to use the solution recommended by mammago, as it is both simple and correct.
Replacing
Bitmap bit = new Bitmap(Image.FromFile("1.png"));
with
Bitmap bit = new Bitmap("1.png");
Should do the trick.
It seems like Image.FromFile() isn't as precise as the Bitmap constructor.
I recently had to check in this monstrosity into production code to manipulate private fields in a WPF class: (tl;dr how do I avoid having to do this?)
private static class MemoryPressurePatcher
{
private static Timer gcResetTimer;
private static Stopwatch collectionTimer;
private static Stopwatch allocationTimer;
private static object lockObject;
public static void Patch()
{
Type memoryPressureType = typeof(Duration).Assembly.GetType("MS.Internal.MemoryPressure");
if (memoryPressureType != null)
{
collectionTimer = memoryPressureType.GetField("_collectionTimer", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as Stopwatch;
allocationTimer = memoryPressureType.GetField("_allocationTimer", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as Stopwatch;
lockObject = memoryPressureType.GetField("lockObj", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);
if (collectionTimer != null && allocationTimer != null && lockObject != null)
{
gcResetTimer = new Timer(ResetTimer);
gcResetTimer.Change(TimeSpan.Zero, TimeSpan.FromMilliseconds(500));
}
}
}
private static void ResetTimer(object o)
{
lock (lockObject)
{
collectionTimer.Reset();
allocationTimer.Reset();
}
}
}
To understand why I would do something so crazy, you need to look at MS.Internal.MemoryPressure.ProcessAdd():
/// <summary>
/// Check the timers and decide if enough time has elapsed to
/// force a collection
/// </summary>
private static void ProcessAdd()
{
bool shouldCollect = false;
if (_totalMemory >= INITIAL_THRESHOLD)
{
// need to synchronize access to the timers, both for the integrity
// of the elapsed time and to ensure they are reset and started
// properly
lock (lockObj)
{
// if it's been long enough since the last allocation
// or too long since the last forced collection, collect
if (_allocationTimer.ElapsedMilliseconds >= INTER_ALLOCATION_THRESHOLD
|| (_collectionTimer.ElapsedMilliseconds > MAX_TIME_BETWEEN_COLLECTIONS))
{
_collectionTimer.Reset();
_collectionTimer.Start();
shouldCollect = true;
}
_allocationTimer.Reset();
_allocationTimer.Start();
}
// now that we're out of the lock do the collection
if (shouldCollect)
{
Collect();
}
}
return;
}
The important bit is near the end, where it calls the method Collect():
private static void Collect()
{
// for now only force Gen 2 GCs to ensure we clean up memory
// These will be forced infrequently and the memory we're tracking
// is very long lived so it's ok
GC.Collect(2);
}
Yes, that's WPF actually forcing a gen 2 garbage collection, which forces a full blocking GC. A naturally occurring GC happens without blocking on the gen 2 heap. What this means in practice is that whenever this method is called, our entire app locks up. The more memory your app is using, and the more fragmented your gen 2 heap is, the longer it will take. Our app presently caches quite a bit of data and can easily take up a gig of memory and the forced GC can lock up our app on a slow device for several seconds -- every 850 MS.
For despite the author's protestations to the contrary, it is easy to arrive at a scenario where this method is called with great frequency. This memory code of WPF's occurs when loading a BitmapSource from a file. We virtualize a listview with thousands of items where each item is represented by a thumbnail stored on disk. As we scroll down, we are dynamically loading in those thumbnails, and that GC is happening at maximum frequency. So scrolling becomes unbelievably slow and choppy with the app locking up constantly.
With that horrific reflection hack I mentioned up top, we force the timers to never be met, and thus WPF never forces the GC. Furthermore, there appear to be no adverse consequences -- memory grows as one scrolls and eventually a GC is triggered naturally without locking up the main thread.
Is there any other option to prevent those calls to GC.Collect(2) that is not so flagrantly hideous as my solution? Would love to get an explanation for what the concrete problems are that might arise from following through with this hack. By that I mean problems with avoiding the call to GC.Collect(2). (seems to me the GC occurring naturally ought to be sufficient)
Notice: Do this only if it causes a bottleneck in your app, and make sure you understand the consequences - See Hans's answer for a good explanation on why they put this in WPF in the first place.
You have some nasty code there trying to fix a nasty hack in the framework... As it's all static and called from multiple places in WPF, you can't really do better than use reflection to break it (other solutions would be much worse).
So don't expect a clean solution there. No such thing exists unless they change the WPF code.
But I think your hack could be simpler and avoid using a timer: just hack the _totalMemory value and you're done. It's a long, which means it can go to negative values. And very big negative values at that.
private static class MemoryPressurePatcher
{
public static void Patch()
{
var memoryPressureType = typeof(Duration).Assembly.GetType("MS.Internal.MemoryPressure");
var totalMemoryField = memoryPressureType?.GetField("_totalMemory", BindingFlags.Static | BindingFlags.NonPublic);
if (totalMemoryField?.FieldType != typeof(long))
return;
var currentValue = (long) totalMemoryField.GetValue(null);
if (currentValue >= 0)
totalMemoryField.SetValue(null, currentValue + long.MinValue);
}
}
Here, now your app would have to allocate about 8 exabytes before calling GC.Collect. Needless to say, if this happens you'll have bigger problems to solve. :)
If you're worried about the possibility of an underflow, just use long.MinValue / 2 as the offset. This still leaves you with 4 exabytes.
Note that AddToTotal actually performs bounds checking of _totalMemory, but it does this with a Debug.Assert here:
Debug.Assert(newValue >= 0);
As you'll be using a release version of the .NET Framework, these asserts will be disabled (with a ConditionalAttribute), so there's no need to worry about that.
You've asked what problems could arise with this approach. Let's take a look.
The most obvious one: MS changes the WPF code you're trying to hack.
Well, in that case, it pretty much depends on the nature of the change.
They change the type name/field name/field type: in that case, the hack will not be performed, and you'll be back to stock behavior. The reflection code is pretty defensive, it won't throw an exception, it just won't do anything.
They change the Debug.Assert call to a runtime check which is enabled in the release version. In that case, your app is doomed. Any attempt to load an image from disk will throw. Oops.
This risk is mitigated by the fact that their own code is pretty much a hack. They don't intend it to throw, it should go unnoticed. They want it to sit quiet and fail silently. Letting images load is a much more important feature which should not be impaired by some memory management code whose only purpose is to keep memory usage to a minimum.
In the case of your original patch in the OP, if they change the constant values, your hack may stop working.
They change the algorithm while keeping the class and field intact. Well... anything could happen, depending on the change.
Now, let's suppose the hack works and disables the GC.Collect call successfully.
The obvious risk in this case is increased memory usage. Since collections will be less frequent, more memory will be allocated at a given time. This should not be a big issue, since collections would still occur naturally when gen 0 fills up.
You'd also have more memory fragmentation, this is a direct consequence of fewer collections. This may or may not be a problem for you - so profile your app.
Fewer collections also means fewer objects are promoted to a higher generation. This is a good thing. Ideally, you should have short-lived objects in gen 0, and long-lived objects in gen 2. Frequent collections will actually cause short-lived objects to be promoted to gen 1 and then to gen 2, and you'll end up with many unreachable objects in gen 2. These will only be cleaned up with a gen 2 collection, will cause heap fragmentation, and will actually increase the GC time, since it'll have to spend more time compacting the heap. This is actually the primary reason why calling GC.Collect yourself is considered a bad practice - you're actively defeating the GC strategy, and this affects the whole application.
In any case, the correct approach would be to load the images, scale them down and display these thumbnails in your UI. All of this processing should be done in a background thread. In the case of JPEG images, load the embedded thumbnails - they may be good enough. And use an object pool so you don't need to instantiate new bitmaps each time, this completely bypasses the MemoryPressure class problem. And yes, that's exactly what the other answers suggest ;)
I think what you have is just fine. Well done, nice hack, Reflection is an awesome tool to fix wonky framework code. I've used it myself many times. Just limit its usage to the view that displays the ListView, it is too dangerous to have it active all the time.
Noodling a bit about the underlying problem, the horrid ProcessAdd() hack is of course very crude. It is a consequence of BitmapSource not implementing IDisposable. A questionable design decision, SO is filled with questions about it. However, about all of them are about the opposite problem, this timer not being quick enough to keep up. It just doesn't work very well.
There isn't anything you can do to change the way this code works. The values it works off are const declarations. Based on values that might have been appropriate 15 years ago, the probable age of this code. It starts at one megabyte and calls "10s of MB" a problem, life was simpler back then :) They forgot to write it so it scales properly, GC.AddMemoryPressure() would probably be fine today. Too little too late, they can't fix this anymore without dramatically altering program behavior.
You can certainly defeat the timer and avoid your hack. Surely the problem you have right now is that its Interval is about the same as the rate at which a user scrolls through the ListView when he doesn't read anything but just tries to find the record of interest. That's a UI design problem that's so common with list views with thousands of rows, an issue you probably don't want to address. What you need to do is cache the thumbnails, gathering the ones you know you'll likely need next. Best way to do that is to do so in a threadpool thread. Measure time while you do this, you can spend up to 850 msec. That code is however not going to be smaller than what you have now, not much prettier either.
.NET 4.6.2 will fix it by killing the MemoryPressure class alltogether. I just have checked the preview and my UI hangs are entirely gone.
.NET 4.6 implemements it
internal SafeMILHandleMemoryPressure(long gcPressure)
{
this._gcPressure = gcPressure;
this._refCount = 0;
GC.AddMemoryPressure(this._gcPressure);
}
whereas pre .NET 4.6.2 you had this crude MemoryPressure class which would force a GC.Collect every 850ms (if in between no WPF Bitmaps were allocated) or every 30s regardless how much WPF bitmaps you did allocate.
For reference the old handle was implemented like
internal SafeMILHandleMemoryPressure(long gcPressure)
{
this._gcPressure = gcPressure;
this._refCount = 0;
if (this._gcPressure > 8192L)
{
MemoryPressure.Add(this._gcPressure); // Kills UI interactivity !!!!!
return;
}
GC.AddMemoryPressure(this._gcPressure);
}
This makes a huge difference as you can see the GC suspension times drop dramatically in a simple test application I did write to repro the issue.
Here you see that the GC suspension times did drop from 2,71s down to 0,86s. This remains nearly constant even for multi GB managed heaps. This also increases overall application performance because now the background GC can do its work where it should: In the background. This prevents sudden halts of all managed threads which can continue to do work happily although the GC is cleaning things up. Not many people are aware what background GC gives them but this makes a real world difference of ca. 10-15% for common application workloads. If you have a multi GB managed application where a full GC can take seconds you will notice a dramatic improvement. In some tests were an application had a memory leak (5GB managed heap, full GC suspend time 7s) I did see 35s UI delays due to these forced GCs!
To the updated question regarding what the concrete problems are you may encounter by using the reflection approach, I think #HansPassant was thorough in his assessment of your specific approach. But more generally speaking, the risk you run with your current approach is the same risk you run with using any reflection against code you don't own; it can change underneath of you in the next update. So long as you're comfortable with that, the code you have should have negligible risk.
To hopefully answer the original question, there may be a way to workaround the GC.Collect(2) problem by minimizing the number of BitmapSource operations. Below is a sample app which illustrates my thought. Similar to what you described, it uses a virtualized ItemsControl to display thumbnails from disk.
While there may be others, the main point of interest is how the thumbnail images are constructed. The app creates a cache of WriteableBitmap objects upfront. As list items are requested by the UI, it reads the image from disk, using a BitmapFrame to retrieve the image information, primarily pixel data. A WriteableBitmap object is pulled from the cache, it's pixel data is overwritten, then it is assigned to the view-model. As existing list items fall out of view and are recycled, the WriteableBitmap object is returned to the cache for later reuse. The only BitmapSource-related activity incurred during that entire process is the actual loading of the image from disk.
It is worth noting that, that the image returned by the GetBitmapImageBytes() method must be exactly the same size as those in the WriteableBitmap cache for this pixel-overwrite approach to work; currently 256 x 256. For simplicity's sake, the bitmap images I used in my testing were already at this size, but it should be trivial to implement scaling, as needed.
MainWindow.xaml:
<Window x:Class="VirtualizedListView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="500" Width="500">
<Grid>
<ItemsControl VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.CleanUpVirtualizedItem="VirtualizingStackPanel_CleanUpVirtualizedItem"
ScrollViewer.CanContentScroll="True"
ItemsSource="{Binding Path=Thumbnails}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="White" BorderThickness="1">
<Image Source="{Binding Image, Mode=OneTime}" Height="128" Width="128" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
namespace VirtualizedListView
{
public partial class MainWindow : Window
{
private const string ThumbnailDirectory = #"D:\temp\thumbnails";
private ConcurrentQueue<WriteableBitmap> _writeableBitmapCache = new ConcurrentQueue<WriteableBitmap>();
public MainWindow()
{
InitializeComponent();
DataContext = this;
// Load thumbnail file names
List<string> fileList = new List<string>(System.IO.Directory.GetFiles(ThumbnailDirectory));
// Load view-model
Thumbnails = new ObservableCollection<Thumbnail>();
foreach (string file in fileList)
Thumbnails.Add(new Thumbnail(GetImageForThumbnail) { FilePath = file });
// Create cache of pre-built WriteableBitmap objects; note that this assumes that all thumbnails
// will be the exact same size. This will need to be tuned for your needs
for (int i = 0; i <= 99; ++i)
_writeableBitmapCache.Enqueue(new WriteableBitmap(256, 256, 96d, 96d, PixelFormats.Bgr32, null));
}
public ObservableCollection<Thumbnail> Thumbnails
{
get { return (ObservableCollection<Thumbnail>)GetValue(ThumbnailsProperty); }
set { SetValue(ThumbnailsProperty, value); }
}
public static readonly DependencyProperty ThumbnailsProperty =
DependencyProperty.Register("Thumbnails", typeof(ObservableCollection<Thumbnail>), typeof(MainWindow));
private BitmapSource GetImageForThumbnail(Thumbnail thumbnail)
{
// Get the thumbnail data via the proxy in the other app domain
ImageLoaderProxyPixelData pixelData = GetBitmapImageBytes(thumbnail.FilePath);
WriteableBitmap writeableBitmap;
// Get a pre-built WriteableBitmap out of the cache then overwrite its pixels with the current thumbnail information.
// This avoids the memory pressure being set in this app domain, keeping that in the app domain of the proxy.
while (!_writeableBitmapCache.TryDequeue(out writeableBitmap)) { Thread.Sleep(1); }
writeableBitmap.WritePixels(pixelData.Rect, pixelData.Pixels, pixelData.Stride, 0);
return writeableBitmap;
}
private ImageLoaderProxyPixelData GetBitmapImageBytes(string fileName)
{
// All of the BitmapSource creation occurs in this method, keeping the calls to
// MemoryPressure.ProcessAdd() localized to this app domain
// Load the image from file
BitmapFrame bmpFrame = BitmapFrame.Create(new Uri(fileName));
int stride = bmpFrame.PixelWidth * bmpFrame.Format.BitsPerPixel;
byte[] pixels = new byte[bmpFrame.PixelHeight * stride];
// Construct and return the image information
bmpFrame.CopyPixels(pixels, stride, 0);
return new ImageLoaderProxyPixelData()
{
Pixels = pixels,
Stride = stride,
Rect = new Int32Rect(0, 0, bmpFrame.PixelWidth, bmpFrame.PixelHeight)
};
}
public void VirtualizingStackPanel_CleanUpVirtualizedItem(object sender, CleanUpVirtualizedItemEventArgs e)
{
// Get a reference to the WriteableBitmap before nullifying the property to release the reference
Thumbnail thumbnail = (Thumbnail)e.Value;
WriteableBitmap thumbnailImage = (WriteableBitmap)thumbnail.Image;
thumbnail.Image = null;
// Asynchronously add the WriteableBitmap back to the cache
Dispatcher.BeginInvoke((Action)(() =>
{
_writeableBitmapCache.Enqueue(thumbnailImage);
}), System.Windows.Threading.DispatcherPriority.Loaded);
}
}
// View-Model
public class Thumbnail : DependencyObject
{
private Func<Thumbnail, BitmapSource> _imageGetter;
private BitmapSource _image;
public Thumbnail(Func<Thumbnail, BitmapSource> imageGetter)
{
_imageGetter = imageGetter;
}
public string FilePath
{
get { return (string)GetValue(FilePathProperty); }
set { SetValue(FilePathProperty, value); }
}
public static readonly DependencyProperty FilePathProperty =
DependencyProperty.Register("FilePath", typeof(string), typeof(Thumbnail));
public BitmapSource Image
{
get
{
if (_image== null)
_image = _imageGetter(this);
return _image;
}
set { _image = value; }
}
}
public class ImageLoaderProxyPixelData
{
public byte[] Pixels { get; set; }
public Int32Rect Rect { get; set; }
public int Stride { get; set; }
}
}
As a benchmark, (for myself if no one else, I suppose) I've tested this approach on a 10-year old laptop with a Centrino processor and had almost no issue with fluidity in the UI.
I wish I could take credit for this, but I believe a better answer is already there: How can I prevent garbage collection from being called when calling ShowDialog on a xaml window?
Even from the code of the ProcessAdd method one can see that nothing gets executed if _totalMemory is small enough. So I think this code is much easier to use and with less side effects:
typeof(BitmapImage).Assembly
.GetType("MS.Internal.MemoryPressure")
.GetField("_totalMemory", BindingFlags.NonPublic | BindingFlags.Static)
.SetValue(null, Int64.MinValue / 2);
We need to understand, though, what the method is supposed to do, and the comment from the .NET source is pretty clear:
/// Avalon currently only tracks unmanaged memory pressure related to Images.
/// The implementation of this class exploits this by using a timer-based
/// tracking scheme. It assumes that the unmanaged memory it is tracking
/// is allocated in batches, held onto for a long time, and released all at once
/// We have profiled a variety of scenarios and found images do work this way
So my conclusion is that by disabling their code, you risk filling up your memory because of the way images are managed. However, since you know that the application you use is large and that it might need GC.Collect to be called, a very simple and safe fix would be for you to call it yourself, when you feel you can.
The code there tries to execute it every time the total memory used goes over a threshold, with a timer so it doesn't happen too often. That would be 30 seconds for them. So why don't you call GC.Collect(2) when you are closing forms or doing other things that would release the use of many images? Or when the computer is idle or the app is not in focus, etc?
I took the time to check where the _totalMemory value comes from and it seems that every time they create a WritableBitmap, they add the memory for it to _totalMemory, which is calculated here: http://referencesource.microsoft.com/PresentationCore/R/dca5f18570fed771.html as pixelWidth * pixelHeight * pixelFormat.InternalBitsPerPixel / 8 * 2; and further on in methods that work with Freezables. It is an internal mechanism to keep track of the memory allocated by the graphical representation of almost any WPF control.
It sounds to me as you could not only set _totalMemory to a very low value, but also hijack the mechanism. You could occasionally read the value, add to it the large value you subtracted initially, and get the actual value of memory used by drawn controls and decide whether you want to GC.Collect or not.
I found a memory leak in my XNA 4.0 application written in C#. The program needs to run for a long time (days) but it runs out of memory over the course of several hours and crashes. Opening Task Manager and watching the memory footprint, every second another 20-30 KB of memory is allocated to my program until it runs out. I believe the memory leak occurs when I set the BasicEffect.Texture property because that is the statement that finally throws the OutOfMemory exception.
The program has around 300 large (512px) textures stored in memory as Texture2D objects. The textures are not square or even powers of 2 - e.g. can be 512x431 - one side is always 512px. These objects are created only at initialization, so I am fairly confident it is not caused by creating/destroying Texture2D objects dynamically. Some interface elements create their own textures, but only ever in a constructor, and these interface elements are never removed from the program.
I am rendering texture mapped triangles. Before each object is rendered with triangles, I set the BasicEffect.Texture property to the already created Texture2D object and the BasicEffect.TextureEnabled property to true. I apply the BasicEffect in between each of these calls with BasicEffect.CurrentTechnique.Passes[0].Apply() - I'm aware that I'm calling Apply() twice as much as I should, but the code is wrapped inside of a helper class that calls Apply() whenever any property of BasicEffect changes.
I am using a single BasicEffect class for the entire application and I change its properties and call Apply() any time I render an object.
First, could it be that changing the BasicEffect.Texture property and calling Apply() so many times is leaking memory?
Second, is this the proper way to render triangles with different textures? E.g. using a single BasicEffect and updating its properties?
This code is taken from a helper class so I've removed all the fluff and only included the pertinent XNA calls:
//single BasicEffect object for entire application
BasicEffect effect = new BasicEffect(graphicsDevice);
// loaded from file at initialization (before any Draw() is called)
Texture2D texture1 = new Texture2D("image1.jpg");
Texture2D texture2 = new Texture2D("image2.jpg");
// render object 1
if(effect.Texture != texture1) // effect.Texture eventually throws OutOfMemory exception
effect.Texture = texture1;
effect.CurrentTechnique.Passes[0].Apply();
effect.TextureEnabled = true;
effect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices1, 0, numVertices1, indices1, 0, numTriangles1);
// render object 2
if(effect.Texture != texture2)
effect.Texture = texture2;
effect.CurrentTechnique.Passes[0].Apply();
effect.TextureEnabled = true;
effect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices2, 0, numVertices2, indices2, 0, numTriangles2);
It's an XNA application, so 60 times per second I call my Draw method, which renders all my various interface elements. This means that I could be drawing between 100-200 textures per frame, and the more textures I draw, the faster I run out of memory, even though I am not calling new anywhere in the update/draw loops. I am more experienced with OpenGL than DirectX, so clearly something is happening behind the scenes that is creating unmanaged memory I'm not aware of.
My only suggestion to you that I can come up with, is to group your textures into atlases rather than one by one. It will speed up your rendering time and reduce the load on the GPU, provided that you are rendering a massive amount of textures per frame. The reason for this, is because the GPU will not have to swap textures to render so often which is an expensive operation. I am using my knowledge of OpenGL, but my guess is that XNA is based on DirectX and I am assuming they load textures in a similar manner (unless you are using Monogame which lets you use OpenGL)
That said, you did not provide very much information. The memory leak can be coming from the texture switching, but it can also be coming from somewhere else. Most of your memory is occupied with textures, which is probably why you are getting a crash there rather than somewhere else. I am guessing a few things are happening here:
The garbage collector is not working fast enough to pick up all the RAM being allocated inside the rendering functions
You have a memory leak somewhere else in your code, and it is showing up here instead
Once again, it is difficult to figure out what is on here provided how little I know about your code. But to my best ability, I have a few suggestions for you:
Run through your code and look at how you are referecing things. Make sure that you don't have any temporary references inside of classes and structs. If you use something and pass it around to difference classes and consider it later as "discarded", there is good chance that someone is still holding onto that object, preventing it from being deleted
Do a search for all the "new" keywords you have in the solution. If you have something that constantly gets using the "new" keyword, this can be a huge memory leak as it's creating a massive ammount of objects in the heap. The garbage collector SHOULD be picking them up, but I wouldn't say I trust the garbage collector very much. Worst case that garbage collector is not coming around often enough to deal with this memory leak.
Find ways to reduce the sizes of textures. Atlasing is a solution that will reduce the overhead of packaging each texture in it's own Texture2D. This can be a bit more work because you will have to work in a system of swapping textures from the same file, but in your case that very well maybe worth it.
If you are convinced that there is an issue within XNA, try a different implementation called Monogame. It follows the exact same structure as XNA but is maintained by a community. As a result, the guts of the libraries you are using have been rewritten and there is a good chance that whatever is destroying your heap has been fixed.
My advice to you? If you are really familiar with OpenGL and what you are doing is fairly simple, then I would check out OpenTK. it is a thin linker layer that takes OpenGL and "ports" it into C#. All the commands are the exact same and you have the flexibility of using the entire .NET library for all the extra fluff.
I hope this helps!
I am currently investigating a performance issue in an application and have highlighted the following;
I have a class -
public static class CommonIcons
{
...
public static readonly System.Windows.Media.ImageSource Attributes = typeof(CommonIcons).Assembly.GetImageFromResourcePath("Resources/attributes.png");
...
}
As a test harness I then have the following code using this class to show the issue -
for (int loop = 0; loop < 20000; loop++)
{
// store time before call
System.Windows.Controls.Image image = new System.Windows.Controls.Image
{
Source = CommonIcons.Attributes,
Width = 16,
Height = 16,
VerticalAlignment = VerticalAlignment.Center,
SnapsToDevicePixels = true
};
// store time after call
// log time between before and after
}
At the start of the loop the time difference is less than 0.001 seconds, but after 20000 goes this has increased to 0.015 seconds.
If I don't use the static member and directly refer to my icon, then I do not have the performance hit, i.e.
for (int loop = 0; loop < 20000; loop++)
{
// store time before call
System.Windows.Controls.Image image = new System.Windows.Controls.Image
{
Source = typeof(CommonIcons).Assembly.GetImageFromResourcePath("Resources/attributes.png"),
Width = 16,
Height = 16,
VerticalAlignment = VerticalAlignment.Center,
SnapsToDevicePixels = true
};
// store time after call
// log time between before and after
}
But in my real world program I don't want to be creating the imagesource on every call (increased memory until a garbage collection), hence why a static member is used. However I also cannot live with the performance hit.
Can someone explain why the original code is creating this performance hit? And also a better solution for what I am trying to do?
Thanks
It smells like something to do with garbage collection. I wonder whether there's some kind of coupling between the ImageSource and the Image which is causing problems in your first case. Have you looked to see what the memory usage of your test harness looks like in each case?
Out of interest, what happens if you set the Source to null at the end of each iteration? I know this is a bit silly, but then that's a natural corollary of it being a test harness :) It might be a further indication that it's a link between the source and the image...
Can you store only constant strings like "Resources/attributes.png" in your CommonIcons class ?
The difference is not between static member or not, but it is that in the first version you create 20000 images all having the same imagesource. I don't know exactly what is going on, but under the hood there may be delegates created automatically which handle communication between imagesource and image and everytime if an event in the imagesource occurs, 20000 clients needs to be notified, so this is a large performance hit.
In the second version, each of the 20000 created images have their own imagesource so you don't experience this overhead.
Note that you should always dispose graphical objects like Images with their Dispose()-method after you are done with them, this will speed up your application a bit and lowers your general memory usage.
I have some code that does something like this (Irrelevant bits snipped):
void foo(Bitmap bmp1, Bitmap bmp2)
{
Bitmap bmp3;
if(something)
bmp3 = new Bitmap(bmp1.Width, bmp1.Height + bmp2.Height);
else
bmp3 = new Bitmap(bmp1.Width, 18000);
(more stuff here that runs fine)
}
Anywho most of the time this ran fine. At first. As the project continued it started to fail on the new Bitmap line. The error it gives is: "ArgumentException was unhandled. Parameter is not valid." There's no mention of which parameter it has a problem with or anything. I'm stumped. Here's what I know for sure:
bmp1 and bmp2 have never been null
when this error is thrown.
The if statement's presence has
never made a difference; it dies
just as frequently without.
Both examples of the constructor use
have thrown this error.
I'm tempted to say this is a memory error, except it doesn't mention anything of the sort. The first dozen times or so this happened the heights totalled over 18000 (hence the magic number above). Figuring it was some kind of soft barrier to our system, we just limited the images at that height which made the exceptions go away after a while.
For some sample data, the exception I'm looking at right now has bmp1.Width at 2550, bmp1.Height at 6135 and bmp2.Height at 6285.
Anyone have any ideas?
GDI+ does not generate very good exception messages. The exception you got is flaky, this one will generate it reliably on my machine:
private void button1_Click(object sender, EventArgs e) {
var bmp = new Bitmap(20000, 20000);
}
What's really going on is that this bitmap requires too much contiguous unmanaged memory to store the bitmap bits, more than is available in your process. On a 32-bit operating system, you can only ever hope to allocate a chunk of memory around 550 megabytes. That goes quickly down-hill from there.
The issue is address space fragmentation, the virtual memory of your program stores a mix of code and data at various addresses. Total memory space is around 2 gigabytes but the biggest hole is much smaller than that. You can only consume all memory with lots of small allocations, big ones fail much quicker.
Long story short: it is trying to tell you that the size you requested cannot be supported.
A 64-bit operating system doesn't have this problem. Be sure to take advantage of it with Project > Properties > Build tab, Platform target = AnyCPU and Prefer 32-bit = unticked. Also, WPF relies on WIC, an imaging library that's a lot smarter about allocating buffers for bitmaps.
Check whether bmp1 or bmp2 has been Disposed (even if not null). See here:
The Mysterious Parameter Is Not Valid Exception
Anyone have any ideas?
Wrap the call that is throwing ArgumentException with a try-catch(Exception ex) and step into the exception block to see the raw exception. It should give you more detail, like which argument is supposedly invalid.
try
{
bmp3 = new Bitmap(bmp1.Width, bmp1.Height + bmp2.Height);
}
catch (Exception ex)
{
throw; // breakpoint here, examine "ex"
}