Simple WPF sample causes uncontrolled memory growth - c#

I have boiled down an issue I'm seeing in one of my applications to an incredibly simple reproduction sample. I need to know if there's something amiss or something I'm missing.
Anyway, below is the code. The behavior is that the code runs and steadily grows in memory until it crashes with an OutOfMemoryException. That takes a while, but the behavior is that objects are being allocated and are not being garbage collected.
I've taken memory dumps and ran !gcroot on some things as well as used ANTS to figure out what the problem is, but I've been at it for a while and need some new eyes.
This reproduction sample is a simple console application that creates a Canvas and adds a Line to it. It does this continually. This is all the code does. It sleeps every now and again to ensure that the CPU is not so taxed that your system is unresponsive (and to ensure there's no weirdness with the GC not being able to run).
Anyone have any thoughts? I've tried this with .NET 3.0 only, .NET 3.5 and also .NET 3.5 SP1 and the same behavior occurred in all three environments.
Also note that I've put this code in a WPF application project as well and triggered the code in a button click and it occurs there too.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows;
namespace SimplestReproSample
{
class Program
{
[STAThread]
static void Main(string[] args)
{
long count = 0;
while (true)
{
if (count++ % 100 == 0)
{
// sleep for a while to ensure we aren't using up the whole CPU
System.Threading.Thread.Sleep(50);
}
BuildCanvas();
}
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void BuildCanvas()
{
Canvas c = new Canvas();
Line line = new Line();
line.X1 = 1;
line.Y1 = 1;
line.X2 = 100;
line.Y2 = 100;
line.Width = 100;
c.Children.Add(line);
c.Measure(new Size(300, 300));
c.Arrange(new Rect(0, 0, 300, 300));
}
}
}
NOTE: the first answer below is a bit off-base since I explicitly stated already that this same behavior occurs during a WPF application's button click event. I did not explicitly state, however, that in that app I only do a limited number of iterations (say 1000). Doing it that way would allow the GC to run as you click around the application. Also note that I explicitly said I've taken a memory dump and found my objects were rooted via !gcroot. I also disagree that the GC would not be able to run. The GC does not run on my console application's main thread, especially since I'm on a dual core machine which means the Concurrent Workstation GC is active. Message pump, however, yes.
To prove the point, here's a WPF application version that runs the test on a DispatcherTimer. It performs 1000 iterations during a 100ms timer interval. More than enough time to process any messages out of the pump and keep the CPU usage low.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
namespace SimpleReproSampleWpfApp
{
public partial class Window1 : Window
{
private System.Windows.Threading.DispatcherTimer _timer;
public Window1()
{
InitializeComponent();
_timer = new System.Windows.Threading.DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(100);
_timer.Tick += new EventHandler(_timer_Tick);
_timer.Start();
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
void RunTest()
{
for (int i = 0; i < 1000; i++)
{
BuildCanvas();
}
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void BuildCanvas()
{
Canvas c = new Canvas();
Line line = new Line();
line.X1 = 1;
line.Y1 = 1;
line.X2 = 100;
line.Y2 = 100;
line.Width = 100;
c.Children.Add(line);
c.Measure(new Size(300, 300));
c.Arrange(new Rect(0, 0, 300, 300));
}
void _timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
RunTest();
_timer.Start();
}
}
}
NOTE2: I used the code from the first answer and my memory grew very slowly. Note that 1ms is much slower and less iterations than my example. You have to let it run for a couple minutes before you start to notice growth. After 5 minutes it's at 46MB from a starting point of 30MB.
NOTE3: Removing the call to .Arrange completely eliminates the growth. Unfortunately, that call is pretty vital to my use since in many cases I'm creating PNG files from the Canvas (via the RenderTargetBitmap class). Without the call to .Arrange it doesn't layout the canvas at all.

I was able to reproduce your problem using the code you provided. Memory keeps growing because the Canvas objects are never released; a memory profiler indicates that the Dispatcher's ContextLayoutManager is holding on to them all (so that it can invoke OnRenderSizeChanged when necessary).
It seems that a simple workaround is to add
c.UpdateLayout()
to the end of BuildCanvas.
That said, note that Canvas is a UIElement; it's supposed to be used in UI. It's not designed to be used as an arbitrary drawing surface. As other commenters have already noted, the creation of thousands of Canvas objects may indicate a design flaw. I realise that your production code may be more complicated, but if it's just drawing simple shapes on a canvas, GDI+-based code (i.e., the System.Drawing classes) may be more appropriate.

WPF in .NET 3 and 3.5 has an internal memory leak. It only triggers under certain situations. We could never figure out exactly what triggers it, but we had it in our app. Apparently it's fixed in .NET 4.
I think it's the same as the one mentioned in this blog post
At any rate, putting the following code in the App.xaml.cs constructor solved it for us
public partial class App : Application
{
public App()
{
new HwndSource(new HwndSourceParameters());
}
}
If nothing else solves it, try that and see

Normally in .NET GC gets triggered on object allocation upon crossing a certain threshold, it does not depend on message pumps (I can't imagine it's different with WPF).
I suspect that Canvas objects are somehow rooted deep inside or something. If you do c.Children.Clear() right before the BuildCanvas method finishes, the memory growth slows down dramatically.
Anyway, as a commenter noted here, such usage of framework elements is pretty unusual. Why do you need so many Canvases?

Edit 2: Obviously not the answer, but was part of the back-and-forth among answers and comments here, so I'm not deleting it.
The GC never gets a chance to collect those objects because your loop and its blocking calls never end, and therefore the message pump and events never get their turn. If you used a Timer of some sort so that messages and events actually have a chance to process, you probably wouldn't be able to eat up all your memory.
Edit: The following does not eat up my memory as long as the interval is greater than zero. Even if the interval is just 1 Tick, as long as it isn't 0. If it's 0, we're back to the infinite loop.
public partial class Window1 : Window {
Class1 c;
DispatcherTimer t;
int count = 0;
public Window1() {
InitializeComponent();
t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds( 1 );
t.Tick += new EventHandler( t_Tick );
t.Start();
}
void t_Tick( object sender, EventArgs e ) {
count++;
BuildCanvas();
}
private static void BuildCanvas() {
Canvas c = new Canvas();
Line line = new Line();
line.X1 = 1;
line.Y1 = 1;
line.X2 = 100;
line.Y2 = 100;
line.Width = 100;
c.Children.Add( line );
c.Measure( new Size( 300, 300 ) );
c.Arrange( new Rect( 0, 0, 300, 300 ) );
}
}

Related

Drawing data obtained from Serial Port progresively getting slower

I am working on voltmeter application, that draws voltage waveform. Hardware sends 1000 numbers (range 0 - 1023, always whole numbers) in string format per second through serial port.
public SerialPort serialPort = new SerialPort("COM3", 57600);
serialPort.Open();
String is converted into int and then drawn with DrawLine into PictureBox.
// variable declarations, all is int, runs in its own thread
while (blToMeasure) // true after clicking on button
{
iPrevY = iY;
iY = Int16.Parse(serialPort.ReadLine());
graphicsGraph.DrawLine(penBlack, iX, iPrevY, iX + 1, iY);
// only this thread is accessing PictureBox
iX++;
if (iX > picBoxGraph.Width)
{
graphicsGraph.Clear(SystemColors.Control);
iX = 0;
}
if (iY > picBoxGraph.Height)
{
}
}
Issue is that drawing lines itself is fast as it should be only for a couple of seconds, but gets gradually slower.
I tried Int.Parse, Int32.Parse and splitting thread function multiple ways using lock (graphicsGraph) (moving conditions with Clear into another thread) or using BlockingCollection<int> (moving DrawLine into another thread, away from Parse). Nothing seems to work and app still gets slower a couple of times after like a minute of running.
There isn't issue with hardware itself, checked with another software. Is this too fast for C#?
Solution:
I got the best results using Port.ReadTimeout = 1 and Port.DiscardInBuffer(). Also using Form.DoubleBuffered = true, but it doesn't make a huge difference in this particular case.
// constructor
Port.ReadTimeout = 1;
Form.DoubleBuffered = true;
Here is the loop itself:
btn.Click() // click to start measuring
{
Port.DiscardInBuffer();
blToMeasure = true;
}
while (blToMeasure) // true after clicking on button
{
iPrevY = iY;
try {
iY = Int16.Parse(serialPort.ReadLine());
}
catch
{
// exception logic
}
graphicsGraph.DrawLine(penBlack, iX, iPrevY, iX + 1, iY);
// only this thread is accessing PictureBox
iX++;
if (iX > picBoxGraph.Width)
{
graphicsGraph.Clear(SystemColors.Control);
iX = 0;
}
if (iY > picBoxGraph.Height)
{
}
}
When the app starts to read from the port, there is always accumulated data, because my hardware is sending numbers all the time, so I get rid of the buffer. Than the drawing of lines is not executed in differing spikes and the speed is constant. Analyzing the issue with Watch, I found out, that it occasionaly takes much longer to read this data and because of 1000 reads per second, it slows down. So to prevent slowing down, I used Port.ReadTimeout, that skips the read, if it takes too long.
The difference is visible, drawing no longer slows down and it keeps the same pace for minutes from what I've tried. I consider this sufficient solution for my issue, thank you!

Flicker in C# WinForms program on screen clear

Alright, so I've done some research into this topic and most of the solutions I've found claim to fix the problem but I am finding that they aren't quite working right. I'm in the early stages of implementing just a simple little particle engine, nothing crazy I'm just doing it out of boredom. I have not done anything like this with WinForms before, I have certainly with C/C++ but this is a new thing for me. The following is the code I am using to draw the particles to the screen, the boiler plate code for the particles is not relevant as it works fine, I am more curious about my actual game loop.
Here is the main code for updates and redraws
public MainWindow()
{
this.DoubleBuffered = true;
InitializeComponent();
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle(object sender, EventArgs e)
{
Graphics g = CreateGraphics();
while (IsApplicationIdle())
{
UpdateParticles();
RenderParticles(g);
g.Dispose();
}
}
//Variables for drawing the particle
Pen pen = new Pen(Color.Black, 5);
Brush brush = new SolidBrush(Color.Blue);
public bool emmiter = false;
private void EmitterBtn_Click(object sender, EventArgs e)
{
//Determine which emitter to use
if (emmiter == true)
{
//Creates a new particle
Particle particle = new Particle(EmitterOne.Left, EmitterOne.Top, .5f, .5f, 20, 20);
emmiter = false;
}
else if(emmiter == false)
{
Particle particle = new Particle(EmitterTwo.Left, EmitterTwo.Top, -.5f, .5f, 20, 20);
emmiter = true;
}
}
public void RenderParticles(Graphics renderer)
{
Invalidate();
Thread.Sleep(0);
//Iterate though the static list of particles
for (int i = 0; i < Particle.activeParticles.Count; i++)
{
//Draw Particles
renderer.DrawRectangle(pen, Particle.activeParticles[i].x,
Particle.activeParticles[i].y,
Particle.activeParticles[i].w,
Particle.activeParticles[i].h);
}
}
public void UpdateParticles()
{
for (int i = 0; i < Particle.activeParticles.Count; i++)
{
//Move particles
Particle.activeParticles[i].MoveParticle();
}
}
The issue I am running into is that anytime the screen is getting cleared and updated, it gets this awful flickering, and not only that but it sometimes won't whenever I emit a particle.
The form is basically just using labels as invisible locations on the screen to say where to render each particle.
Anyway, I've seen this topic before but nothing has fixed anything, the current implementation is the least flickery/laggy but is not solving the issue.
Any help is appreciated, thanks!
EDIT* I realized I was never deallocating the graphics object each loop so I did that and there is no more delay whenever I click the emitter button, however the flicker is still there, I updated the code accordingly.
Getting rid of the visible paint artifacts requires double-buffering. In other words, render the scene into a back-buffer that, when ready, gets quickly blitted to the screen surface in a single step. That's a built-in feature in Winforms, simply set the DoubleBuffered property to true in the form constructor. You must use the Paint event to take advantage of that. Override OnPaint() and call RenderParticles(e.Graphics).
You need to take care of timing, right now your UI thread is burning 100% core and animation speed completely depends on the number of particles and the speed of the machine. Instead of Application.Idle, drop a Timer from the toolbox onto your form. In the Tick event handler, call UpdateParticles() and this.Invalidate() to get the Paint event to fire again. The timer's Interval property value is critical, you get the most reproducible update rate by picking 15 or 31 msec (64 or 32 FPS).
You are not always going to get the desired FPS rate, the timer will simply delay or skip a Tick event if the machine gets busy or is too slow or other code on the UI thread needs to run. To make sure that doesn't affect the animation, you must measure actual elapsed time instead of moving the particles by a fixed amount. Either Environment.TickCount, DateTime.UtcNow or Stopwatch are suitable ways to measure true elapsed time.

Making a constantly updating Image (WPF)

So i am messing around with making a simple AI and stuff with basic C#, and in this project i have alot of points which i've been visualising by making a bitmap.
This bitmap i have rendered/loaded to an image object in the WPF window.. but my problem is that this is rendered each milisecond, making the framerate quite bad - so how would i make this better?
Can i load it 'constantly'? or should i take another approach?
What i got now is pretty simple but i can show the important parts anyway, taken out of the full class:
private static Bitmap BitMap = new Bitmap(500, 500);
static Graphics GraphicFromBitMap
{
get
{
return Graphics.FromImage(BitMap);
}
}
public static BitmapSource loadBitmapAsImage()
{
IntPtr intPtr = BitMap.GetHbitmap();
BitmapSource bitmapSource = null;
try
{
bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(intPtr,
IntPtr.Zero, Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(intPtr);
}
DeleteObject(intPtr);
return bitmapSource;
}
This is ofcourse only the bitmap part - the actual loading is done the following way:
DispatcherTimer Timer = new DispatcherTimer();
public MainWindow()
{
this.Timer.Tick += new EventHandler(Timer_Tick);
this.Timer.Interval = new TimeSpan(0, 0, 0, 1);
this.Timer.Start();
}
void Timer_Tick(object sender, EventArgs e)
{
WorldMap.Draw();
map.Source = WorldMap.BitMapSource;
}
This is ofcourse only the important parts - i hope my question is understandable but just to clearify and repeat:
I need a WPF image to update 'every frame' or everytime specific values change.
My question might have been answered before, but i couldn't really find anything that work nor something suiting this instance.
BTW making the timer set off more frequently creates an error with the loading, but the exact error code, i can't remember, and i can't seem to create it again - never the less this probably isn't the most practical way of doing this.
EDIT:
For clarification, this is all i got right now: http://imgur.com/EIiSRFQ
I want nothing fancy - it's just for personal projects playing around with programming and math, and that's alot easier if i can visualize the objects that i am 'moving' in my 2D plane.
Right now i am playing around with physics and gravity, trying to create a simple solar system with working physics. this is all just side projects to get to know the different tools better when i am too tired to work on my main project.
I would look to represent the visual elements of the bitmap as controls in WPF. That way you can update them directly and as frequently as they change, without the overhead of creating a bitmap and rendering it.
Performance would be far greater as you'd only update the changes in value.
To demonstrate the point, create a control...
<UserControl x:Class="Sample_Chart.Views.CodeBehindChart"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Canvas x:Name="LayoutRoot" />
</UserControl>
This is as simple as they get. Next edit the code behind file...
public partial class CodeBehindChart : UserControl
{
public CodeBehindChart()
{
InitializeComponent();
Respond();
}
private async void Respond()
{
await Task.Delay(2000);
Random r = new Random();
while (true)
{
this.LayoutRoot.Children.Clear();
for (int i = 0; i < 100; i++)
{
Rectangle rectangle = new Rectangle();
rectangle.SetValue(Canvas.LeftProperty, r.NextDouble() * this.LayoutRoot.ActualWidth);
rectangle.SetValue(Canvas.TopProperty, r.NextDouble() * this.LayoutRoot.ActualHeight);
rectangle.Width = 2;
rectangle.Height = 2;
rectangle.Fill = Brushes.Black;
this.LayoutRoot.Children.Add(rectangle);
}
await Task.Delay(500);
}
}
}
In this code behind we have an async void method which firstly waits for 2 seconds (optional) before then creating 100 visual elements at random locations within the control. It refreshes this every 1/2 second.
If you did the same thing, but based those locations, sizes and fill - even use different shapes - I think you'll have a high performing, scaling and easily extendable solution to your requirements.
Taking this the next stage and controlling from a ViewModel will require a bit more thought which for your 'first project' - albeit an interesting one, may be a step ambitious ;)

Multithreading System.Windows.Graphics

I know of course that I can not draw onto the same Graphics object from different threads, but is it also true that I can not draw to different Graphics objects in different threads?
Consider the following console program:
class Program
{
static ThreadDrawer[] drawers;
static void Main(string[] args)
{
int numThreads = 8;
drawers = new ThreadDrawer[numThreads];
for (int i = 0; i < numThreads; i++)
{
drawers[i] = new ThreadDrawer();
drawers[i].Start();
}
for (int i = 0; i < numThreads; i++)
{
drawers[i].Wait();
}
Console.WriteLine("Complete.");
Console.ReadKey();
}
class ThreadDrawer
{
private Thread thread;
private AutoResetEvent resetEvent;
public ThreadDrawer()
{
thread = new Thread(DrawRandomCircles);
resetEvent = new AutoResetEvent(false);
}
public void Start()
{
thread.Start();
}
public void Wait()
{
resetEvent.WaitOne();
}
public void DrawRandomCircles()
{
Random r = new Random(Environment.TickCount);
using (Bitmap b = new Bitmap(1000, 1000))
using (Graphics g = Graphics.FromImage(b))
{
for (int i = 0; i < 100000; i++)
{
g.DrawEllipse(Pens.Red, new Rectangle(r.Next(1000), r.Next(1000), 200, 200));
}
}
resetEvent.Set();
}
}
}
The program creates a Bitmap in each thread and proceeds to draw random ellipses on it using a Graphics object, also generated per thread from the Bitmap.
Due to a requirement to build for .net2 the multithreading is implemented using Threads and AutoResetEvents instead of TPL.
The program executes without throwing an exception, but it executes serially. Using n threads multiplies execution time by n and it is clear to see using the task manager that only one core is being used.
Important to take note that none of this is tied to any UI element.
What is going on here? Is the Graphics object locking on a static object?
Here's a screen-shot of the concurrency analyzer I used to see what's going on with these threads:
Yes, you can see lots of red (blocking) with flecks of green (execution). The threads are taking turns entering a critical section that's acquired inside the internal GpGraphics::RenderDrawPath() function. The larger blobs of green is where the program actually drew the lines (I replaced DrawEllipse with DrawRectangle and got rid of the Random call).
There is some concurrency, you can for example see the RenderDrawPath() call being overlapped by the code that renders the anti-aliased lines, overall cpu load is around 35%. But there isn't much of it.
Nothing you can do about it of course. You get ahead by overlapping the logic in your own program to decide what to draw with the GDI+ calls. Which will normally happen, the test is too synthetic.
It seems like locking happens in unmanaged code, inside GDI+ library (unfortunately, this behavior is not mentioned in official docs).
Similar question: Parallelizing GDI+ Image Resizing .net
I'm not 100% sure.. but yes, there is a private static locking object in the Graphics class. It appears to be locked only from GetHalftonePalette, which in turn, is called whenever a Bitmap is initialized within the Graphics object. It would appear that this could be the cause of contention.
(Note: Initial findings after 5 minutes of using ILSpy.. not very in-depth)

What are my options for timing?

I'm making a TextBox control in XNA and do not have access to the GameTime class. Currently I am trying to simulate the blinking text cursor caret and have successfully done so using this code:
int deltaTickCount = Environment.TickCount - previousTickCount;
if (deltaTickCount < CursorBlinkRate && Selected)
{
spriteBatch.Draw(emptyPixel, new Rectangle(caretLocation, Rectangle.Y + 1, caretWidth, caretHeight), Color.Black);
}
else if (deltaTickCount > CursorBlinkRate * 2)
{
previousTickCount = Environment.TickCount;
}
However, I'm a bit wary of using Environment.TickCount. If the computer was running long enough, wouldn't the program eventually crash or produce unpredictable behavior when the tick count exceeded its integral size?
Does anyone know what Windows does? I imagine it would use the system clock. Would that be a more suitable solution? I imagine they used something like total milliseconds in to the second instead of the tick count, but I'm not sure.
Thanks for reading.
I generally use the system diagnostics timer in a lot of situations.
It's a pretty powerful tool which creates a timer for you with a lot of good controls.
using System.Diagnostics;
Stopwatch timer = new Stopwatch();
Then use inbuilt controls:
timer.Start();
if(timer.elapsedMilliseconds() > ...)
{ }
timer.Reset();
etc...
This would allow you to reset the timer?
When Evnironment.TickCount rolls over, deltaTickCount will end up being negative, so you know it has happened. The calculation then becomes:
if (deltaTickCount < 0)
deltaTickCount = int.MaxValue - previousTickCount + Environment.TickCount;
Without bothering with what would happen in the case of an integer overflow, simply change to:
int deltaTickCount =
Environment.TickCount > previousTickCount
? Environment.TickCount - previousTickCount
: CursorBlinkRate * 3;

Categories

Resources