Why doesn't my WPF application free up its memory? - c#

I don't understand why my test WPF application here doesn't free up the memory it uses after I have closed the MainWindow and set it to null, and even run the garbage collector?
In the beginning, even before the MainWindow is created, the Application takes almost no memory, about 5 MB, but when I create the first window it takes 43 MB and it stays there for the rest of the application's life. Isn't it possible to get it back down to 5 MB again without restarting the application?
Diagnostic Tools
public App()
{
ShutdownMode = ShutdownMode.OnExplicitShutdown;
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 2);
Thread.Sleep(2000);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
if (MainWindow == null)
{
MainWindow = new MainWindow();
MainWindow.Show();
}
else
{
MainWindow.Close();
MainWindow = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}

You have a memory leak, so the GC will not be able to collect your MainWindow. You must unsubscribe from your event handler. So keep the timer in a backing field:
MainWindow = null;
// Add this:
this.dispatcherTimer.Tick -= new EventHandler(dispatcherTimer_Tick);
this.dispatcherTimer.Stop();

Related

WPF InitializeComponent doesn't seem to work with DispatcherTimer

I am new to C# and also to WPF, I am trying to understand how DispatcherTimer works with GUI (WPF). I want to make my application run a function every 2 seconds but still using the app itself, when I try the following code, the timer starts but I can't use the following Buttons (logout and exit), it's like the app freezes.
public MainLoggedWindow()
{
Globals.mainLoggedWindow = this;
InitializeComponent();
DispatcherTimer dt = new DispatcherTimer();
dt.Tick += new EventHandler(dtTiker);
dt.Interval = new TimeSpan(0, 0, 1);
dt.Start();
}
private void exit_button_Click(object sender, RoutedEventArgs e)
{
logout_button_Click(sender, e);
Environment.Exit(-1);
}
private void logout_button_Click(object sender, RoutedEventArgs e)
{
Globals.LOGGED_IN_USER.logout();
this.Hide();
Globals.mainWindow.Show();
}
private int increment = 0;
private void dtTiker(object sender,EventArgs e)
{
increment++;
Time.Content = increment.ToString();
}
DispatcherTimer runs on the UI thread. It means that when the DispatcherTimer invokes its Tick method the UI thread becomes busy handling that, and it doesn't have time to handle other UI input like button clicks, so the window freezes.
What you could do is increase the interval time for your DispatcherTimer - your question desription says that it's once every two seconds, but your initialisation logic has it every one second: dt.Interval = new TimeSpan(0, 0, 1);

How to check whether the logged-in user is interactive or idle in wpf application

In WPF application, I let the user to log-in but how can i check whether the logged-in user is interactive or idle in my application?
Interactive: If user is using the application in every minute.
Idle: If user does not use application in one minute.
Possible Solution:
One possible solution can be that we set a timer which continuously counts the duration and saved back to database.
But I do not want to add an overhead of timer to my application as it already has high processing time. Further more, if user just shut down PC without loggin-out from application, the database storage part can be skipped.
Timer is not that bad, try something like this.
You can find client Idle handler class on here : ClientIdleHandler.cs
class GetIdleTime
{
private DispatcherTimer dispatcherTimer;
private ClientIdleHandler _clientIdleHandler;
//call for idle time
public void callForIdletime()
{
_clientIdleHandler = new ClientIdleHandler();
_clientIdleHandler.Start();
//start timer
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += TimerTick;
dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 10);
dispatcherTimer.Start();
}
private void TimerTick(object sender, EventArgs e)
{
if (_clientIdleHandler.IsActive)//active
{
//What you gonna do when idle
}
}
}
You can do
var timer = new DispatcherTimer
(
TimeSpan.FromMinutes(5),
DispatcherPriority.ApplicationIdle,// Or DispatcherPriority.SystemIdle
(s, e) => { mainWindow.Activate(); }, // or something similar
Application.Current.Dispatcher
);

WPF ContentControl some unclears

i have some usercontrols in my solution, in main window i just change content of ContentControl.
in one usercontrol there is a timer
BringTrinket trincket = new BringTrinket();
trincket.TrincketBringed += new TrincketBringedEventHandler(trincket_TrincketBringed);
this.contentSwitcher.Content = new BringTrinket();
}
void trincket_TrincketBringed(object sender, TrincketEventArgs e)
{
MessageBox.Show(e.TrincketNumber);
this.contentSwitcher.Content = new Loading();
}`
after some event Main Window should change content to (new Loading()), it's OK!
public partial class BringTrinket : UserControl, ISwitchable
{
public event TrincketBringedEventHandler TrincketBringed;
private DispatcherTimer timer;
public BringTrinket()
{
InitializeComponent();
/////////////////////////////////////////////////////////////////////////////
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 1800);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
/////////////////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////////////
void timer_Tick(object sender, EventArgs e)
{
if (TrincketBringed != null)
{
TrincketBringed(this, new TrincketEventArgs("Hello"));
//TrincketBringed = null;
}
}
But after i've navigataged Timer in BringTrinket still working, how have i dispose that usercontrol? (I know i can set TrincketBringed to null, but timer will still be working)
I think not only timer, but also usercontrol remains in memory
So, your question is how to stop the timer? Use timer.Stop().
BTW, your code would read better if you rename TricketBringed to TrincketBrought.
It looks like your timer is inside of your first usercontrol. You would either need to stop the timer before losing the reference to that object, or better yet, move the timer out of your control.

Timer, event and garbage collection : am I missing something?

Consider the following code :
class TestTimerGC : Form
{
public TestTimerGC()
{
Button btnGC = new Button();
btnGC.Text = "GC";
btnGC.Click += (sender, e) => GC.Collect();
this.Controls.Add(btnGC);
System.Windows.Forms.Timer tmr = new System.Windows.Forms.Timer();
tmr.Interval = 1000;
tmr.Tick += (sender, e) => this.Text = DateTime.Now.ToString();
tmr.Start();
}
}
If I'm not mistaken, after the tmr variable goes out of scope, the Timer isn't referenced anywhere, so it should be eligible for garbage collection. But when I click the GC button, the timer continues to run, so I guess it wasn't collected...
Does anyone have an explanation for that ?
PS: it's not a real program of course, I was just trying to prove a point to someone... but my proof didn't work ;)
OK, I think I know what's going on... I looked at the code of the Timer class with Reflector, and I found the following instruction in the setter of the Enabled property :
this.timerRoot = GCHandle.Alloc(this);
So, when it is started, the timer allocates a GCHandle for itself, which prevents its collection by the GC...

Memory Leaks in .NET CF when running forms on separate threads

EDIT - nulled thread before measuring finishing memory
This is all running .NET Compact Framework 2.0 under Windows CE 5.0.
I've come across some interesting behaviour in developing my App. Whenever I try to create a form and have it run a separate thread it seems to leak 392 bytes when it's closed and I'm unsure why.
My approach so far has been create a new thread and have it a) Create the form and continuously call Application.DoEvents until it shuts or b) Create the form pass it to Application.Run(Form).
The following is a sample form that illustrates the problem.
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
}
private void DoMemoryTest(bool useApplicationRun)
{
GC.WaitForPendingFinalizers();
GC.Collect();
long originalMem = GC.GetTotalMemory(true);
Thread t;
if (useApplicationRun)
t = new Thread(new ThreadStart(AppRunThread));
else
t = new Thread(new ThreadStart(DoEventThread));
t.Start();
Thread.Sleep(3000);//Dodgey hack
t.Join();
t = null;
GC.WaitForPendingFinalizers();
GC.Collect();
long terminatingMem = GC.GetTotalMemory(true);
MessageBox.Show(String.Format("An increase of {0} bytes was measured from {1} bytes",
terminatingMem - originalMem, originalMem));
}
private void button1_Click(object sender, EventArgs e)
{
DoMemoryTest(false);
}
private void button2_Click(object sender, EventArgs e)
{
DoMemoryTest(true);
}
private void AppRunThread()
{
Application.Run(new OpenCloseForm());
}
private void DoEventThread()
{
using (OpenCloseForm frm = new OpenCloseForm())
{
frm.Show();
do
{
Application.DoEvents();
} while (frm.Showing);
}
}
/// <summary>
/// Basic form that opens for a short period before shutting itself
/// </summary>
class OpenCloseForm : Form
{
public OpenCloseForm()
{
this.Text = "Closing Soon";
this.Size = new Size(100, 100);
this.TopMost = true;
}
public volatile bool Showing = false; //dodgy hack for DoEventThread
System.Threading.Timer timer;
protected override void OnLoad(EventArgs e)
{
Showing = true;
base.OnLoad(e);
timer = new System.Threading.Timer(new TimerCallback(TimerTick), null, 1000, 1000);
}
delegate void CloseDelegate();
private void TimerTick(object obj)
{
this.Invoke(new CloseDelegate(this.Close));
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Showing = false;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (timer != null)
{
timer.Dispose();
timer = null;
}
}
base.Dispose(disposing);
}
}
//Designer code to follow....
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.timer1 = new System.Windows.Forms.Timer();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(32, 47);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(116, 39);
this.button1.TabIndex = 1;
this.button1.Text = "DoEvents Loop";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(32, 115);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(116, 39);
this.button2.TabIndex = 2;
this.button2.Text = "Application.Run";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// TestForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.AutoScroll = true;
this.ClientSize = new System.Drawing.Size(177, 180);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "TestForm";
this.Text = "TestForm";
this.TopMost = true;
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
}
Am I missing something in regards to the disposal of controls? Does anyone have any ideas on where to go from this?
Thanks in advance.
Alrighty, the first thing I have to say about this code is WTF?! You need to do some research into how Windows messages work. I'll cover a little bit here, but you really need to understand it before diving in and trying to crazy stuff like what I'm seeing here.
When a Window is created, all "actions" for that Window are delivered via Windows Messages. When you call Refresh, or Click or whatever, that is a Windows Message.
These messages are dispatched from a "message pump" which is simply a loop that calls PeekMessage, GetMEsssage, TranslateMessage and DispatchMessage APIs.
Application.Run does this in managed code.
Windows Messages for a Form must be delivered in the same thread context as the window was created. That means the pump must also be on the same thread. This is why Control.Invoke exists.
A pump on one thread will no even see messages to a window on another thread
So there are certainly some issues with what you're doing. I'm not certain what the "big picture" of what you're trying to achieve is and how you may have noticed this bug, but this code tells me you've got some fundamental problems in your architecture.
But what about this leak you think you've found anyway? Well, there is no leak. You're not understanding CF (and managed) memory management. Again, research is recommended, but MSDN has a really good webcast that covers it well.
The short of it for this scenario is that you have created some objects on separate threads. Those threads create a bunch of things, some of which are IDisposable, some not. When the thread tears down, those items no longer have roots are therefore available for collection. When Collect is called, the GC then walks all the roots and notes every object that has a root (mark). Those that do not are then "freed"(sweep). The region in the GC heap that they were is in simply no longer marked as in use - see the webcast for details. If the item implementred IDisposabe, it then gets a new root so the finalizer can still exist and run on the next GC cycle. And lastly the finalizer thread runs (non-deterministically).
You code does not account for this behavior. You've not run Collect twice. You've not waited for finalizers after the Collect call (and simply calling WaitForPendingFinalizers may not be enough). Since your threads themselves are not marked as background threads, who knows what their position in their life cycle and state of GC usage might be.
So when we really get down to it, the question is this: what exactly are you trying to solve? You are running in a managed memory environment. Unless you're seeing OOM's you almost always shouldn't be worrying about memory levels - that's the whole point of having a GC in the first place. Don't try building a convoluted, where's Waldo academic exercise and have us try to find the leak.
If you are actually having a problem, then you should first make sure your application design is something in line with the way Windows applications should be written and then use tools like RPM to profile what roots are holding the memory and fix the leaks you have created (yes, leaks still can and do happen in managed code). Of course you can always ask reasonable questions about the real world problem here as well.
EDIT
Microsoft seems to have purged the webcast content I refer to above. Hopefully they'll be able to find it and re-post it, but in mean meantime (and if they never find it) I at least have the PowerPoint I used for the original talk at MEDC and it's available on my blog.
I might be wrong as I'm not an expert in CF, but .net won't neccessarily release the memory that it has taken unless the system is under memory pressure.
My understanding was that if the app needed x bytes of memory once, it will probably need at least x bytes again at some point. It wont release that memory unless something else in the OS needs it.
First, I'd ask why you're creating enough forms on other threads that you care about 392 bytes, but that's beside the point I guess.
The first place I'd start at is that managed thread instance that you don't get rid of in the DoMemoryTest method. Call t.Join() after your Thread.Sleep(3000) call, then set it to null (so it can be GCed even in debug mode).

Categories

Resources