I have some unit tests to verify if objects using WeakReferences work correctly. After refactoring these objects to work asynchronously the unit tests fail. This seems to be caused by GC.WaitForPendingFinalizers not working (or working differently?) when using async.
To check this I created a simple WPF app with two buttons, one with a regular Click event and one with an async click event.
When I press the NormalGCTest button "object garbage collected: True" is shown.
But when I press the AsyncGCTest button "object garbage collected: False" is shown.
What is going on? Is there a way to force a full garbage collection in my tests?
private void NormalGCTest(object sender, RoutedEventArgs e)
{
var temp1 = new object();
var temp2 = new WeakReference(temp1);
temp1 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
temp1 = temp2.Target;
System.Diagnostics.Debug.WriteLine("object garbage collected: {0}", temp1 == null);
}
private async void AsyncGCTest(object sender, RoutedEventArgs e)
{
var temp1 = new object();
var temp2 = new WeakReference(temp1);
temp1 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
temp1 = temp2.Target;
System.Diagnostics.Debug.WriteLine("object garbage collected: {0}", temp1 == null);
await Task.Delay(0);
}
For me, it works fine already - I get True / True. You could, however, try a few things to clarify what is happening - for example:
var wr = CreateWeakReference();
Console.WriteLine("object available: {0}", wr.Target != null);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
Console.WriteLine("object garbage collected: {0}", wr.Target == null);
with
static WeakReference CreateWeakReference()
{
return new WeakReference(new object());
}
This uses less locals, which could cause confusion depending on how the compiler translates the C#. It also uses a more aggressive GC.Collect.
But important: stop invoking the garbage collector. You should almost never do this. Final thought - you might want to avoid async void. Yes, I know this is an event-handler, but a nice trick there is to immediate call into a non-async void method (using await if necessary).
Related
I have spent a few days now finding a bug that freezes my companies application. The dreaded UserPreferenceChanged UI freeze. It's not a complicated bug, but hard to find in a rather big application. There are quite a few articles about how this bug unfolds but not on how to put ones finger on the faulty code. I have put together a solution, in form of a logging mechanism from multiple older tickets and (i hope) improved a bit upon them. May it save some time for the next programmer with this problem.
How to recognize the bug?
The application freezes completely. Nothing more to be done than create a memory dump and then close it via TaskManager. If you open the dmp file in VisualStudio or WinDbg you might see a stack trace like this one
WaitHandle.InternalWaitOne
WaitHandle.WaitOne
Control.WaitForWaitHandle
Control.MarshaledInvoke
Control.Invoke
WindowsFormsSynchronizationContext.Send
System.EventInvokeInfo.Invoke
SystemEvents.RaiseEvent
SystemEvents.OnUserPreferenceChanged
SystemEvents.WindowProc
:
The important two lines here are "OnUserPreferenceChanged" and "WindowsFormsSynchronizationContext.Send"
What's the cause?
SynchronizationContext was introduced with .NET2 to generalize thread synchronization. It gives us methods like "BeginInvoke" and such.
The UserPreferenceChanged event is rather self explanatory. It will be triggered by the user changing his background, logging in or out, changing the Windows accent colors and lots of other actions.
If one creates a GUI control on a background thread a WindowsFormsSynchronizationContext is installed on said thread. Some GUI controls subscribe to the UserPreferenceChanged event when created or when using certain methods. If this event is triggered by the user the main thread sends a message to all subscribers and waits. In the described scenarion: a worker thread without a message loop! The application is frozen.
To find the cause of the freeze can be especially hard because the cause of the bug (creation of GUI element on a background thread) and the error state (application frozen) can be minutes apart. See this really good article for more details and a slightly different scenario. https://www.ikriv.com/dev/dotnet/MysteriousHang
Examples
How can one provoke this error for testing purposes?
Example 1
private void button_Click(object sender, EventArgs e)
{
new Thread(DoStuff).Start();
}
private void DoStuff()
{
using (var r = new RichTextBox())
{
IntPtr p = r.Handle; //do something with the control
}
Thread.Sleep(5000); //simulate some work
}
Not bad but not good either. If the UserPreferenceChanged event gets triggered in the few milliseconds you use the RichTextBox your application will freeze. Could happen, not very likely though.
Example 2
private void button_Click(object sender, EventArgs e)
{
new Thread(DoStuff).Start();
}
private void DoStuff()
{
var r = new RichTextBox();
IntPtr p = r.Handle; //do something with the control
Thread.Sleep(5000); //simulate some work
}
This is bad. The WindowsFormsSynchronizationContext gets not cleaned up because the RichTextBox does not get disposed. If the UserPreferenceChangedEvent occures while the thread lives your application will freeze.
Example 3
private void button_Click(object sender, EventArgs e)
{
Task.Run(() => DoStuff());
}
private void DoStuff()
{
var r = new RichTextBox();
IntPtr p = r.Handle; //do something with the control
}
This is a nightmare. Task.Run(..) will execute the work on a background thread on the threadpool. The WindowsFormsSynchronizationContext gets not cleaned up because the RichTextBox is not disposed. Threadpool threads are not cleaned up. This background thread now lurks in your threadpool just waiting for the UserPreferenceChanged event to freeze your application even long after your task has returned!
Conclusion: Risk is manageable when you know what you do. But whenever possible: avoid GUI Elements in a background thread!
How to deal with this bug?
I put together a solution from older tickets. Thanks very much to those guys!
WinForms application hang due to SystemEvents.OnUserPreferenceChanged event
https://codereview.stackexchange.com/questions/167013/detecting-ui-thread-hanging-and-logging-stacktrace
This solution starts a new thread that continuously tries to detect any threads which are subscribed to the OnUserPreferenceChanged Event and then provide a call stack that should tell you why that is.
public MainForm()
{
InitializeComponent();
new Thread(Observe).Start();
}
private void Observe()
{
new PreferenceChangedObserver().Run();
}
internal sealed class PreferenceChangedObserver
{
private readonly string _logFilePath = $"filePath\\FreezeLog.txt"; //put a better file path here
private BindingFlags _flagsStatic = BindingFlags.NonPublic | BindingFlags.Static;
private BindingFlags _flagsInstance = BindingFlags.NonPublic | BindingFlags.Instance;
public void Run() => CheckSystemEventsHandlersForFreeze();
private void CheckSystemEventsHandlersForFreeze()
{
while (true)
{
try
{
foreach (var info in GetPossiblyBlockingEventHandlers())
{
var msg = $"SystemEvents handler '{info.EventHandlerDelegate.Method.DeclaringType}.{info.EventHandlerDelegate.Method.Name}' could freeze app due to wrong thread. ThreadId: {info.Thread.ManagedThreadId}, IsThreadPoolThread:{info.Thread.IsThreadPoolThread}, IsAlive:{info.Thread.IsAlive}, ThreadName:{info.Thread.Name}{Environment.NewLine}{info.StackTrace}{Environment.NewLine}";
File.AppendAllText(_logFilePath, DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss") + $": {msg}{Environment.NewLine}");
}
}
catch { }
}
}
private IEnumerable<EventHandlerInfo> GetPossiblyBlockingEventHandlers()
{
var handlers = typeof(SystemEvents).GetField("_handlers", _flagsStatic).GetValue(null);
if (!(handlers?.GetType().GetProperty("Values").GetValue(handlers) is IEnumerable handlersValues))
yield break;
foreach(var systemInvokeInfo in handlersValues.Cast<IEnumerable>().SelectMany(x => x.OfType<object>()).ToList())
{
var syncContext = systemInvokeInfo.GetType().GetField("_syncContext", _flagsInstance).GetValue(systemInvokeInfo);
//Make sure its the problematic type
if (!(syncContext is WindowsFormsSynchronizationContext wfsc))
continue;
//Get the thread
var threadRef = (WeakReference)syncContext.GetType().GetField("destinationThreadRef", _flagsInstance).GetValue(syncContext);
if (!threadRef.IsAlive)
continue;
var thread = (Thread)threadRef.Target;
if (thread.ManagedThreadId == 1) //UI thread
continue;
if (thread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId)
continue;
//Get the event delegate
var eventHandlerDelegate = (Delegate)systemInvokeInfo.GetType().GetField("_delegate", _flagsInstance).GetValue(systemInvokeInfo);
//Get the threads call stack
string callStack = string.Empty;
try
{
if (thread.IsAlive)
callStack = GetStackTrace(thread)?.ToString().Trim();
}
catch { }
yield return new EventHandlerInfo
{
Thread = thread,
EventHandlerDelegate = eventHandlerDelegate,
StackTrace = callStack,
};
}
}
private static StackTrace GetStackTrace(Thread targetThread)
{
using (ManualResetEvent fallbackThreadReady = new ManualResetEvent(false), exitedSafely = new ManualResetEvent(false))
{
Thread fallbackThread = new Thread(delegate () {
fallbackThreadReady.Set();
while (!exitedSafely.WaitOne(200))
{
try
{
targetThread.Resume();
}
catch (Exception) {/*Whatever happens, do never stop to resume the target-thread regularly until the main-thread has exited safely.*/}
}
});
fallbackThread.Name = "GetStackFallbackThread";
try
{
fallbackThread.Start();
fallbackThreadReady.WaitOne();
//From here, you have about 200ms to get the stack-trace.
targetThread.Suspend();
StackTrace trace = null;
try
{
trace = new StackTrace(targetThread, true);
}
catch (ThreadStateException) { }
try
{
targetThread.Resume();
}
catch (ThreadStateException) {/*Thread is running again already*/}
return trace;
}
finally
{
//Just signal the backup-thread to stop.
exitedSafely.Set();
//Join the thread to avoid disposing "exited safely" too early. And also make sure that no leftover threads are cluttering iis by accident.
fallbackThread.Join();
}
}
}
private class EventHandlerInfo
{
public Delegate EventHandlerDelegate { get; set; }
public Thread Thread { get; set; }
public string StackTrace { get; set; }
}
}
Attention
1)This is a very ugly hack. It deals with threads in a very invasive way. It should never see a live customer system. I was already nervous deploying it to the customers test system.
2)If you get a logfile it might be very big. Any thread might cause hundreds of entries. Start at the oldest entries, fix it and repeat.(Because of the "tainted thread" scenario from Example 3 it might also contain false positives)
3)I am not sure about the performance impact of this hack. I assumed it would be very big. to my surprise it was almost not noteable. Might be different on other systems though
I've got some code like this:
private void button1_Click(object sender, EventArgs e)
{
Someclass object= new Someclass();
foreach (conditioin)
{
some methods();
}
object= null;
}
I need to free memory of object becuase every time that I press the button it shows me the same results. But methods inside should be forget and initialised with new result all over again.
Can you help me, please?
$$ UPDATE $$
OK so I'll provide the specific code
I use NativeWiFi
private void button1_Click(object sender, EventArgs e)
{
listView1.Items.Clear();
WlanClient client = new WlanClient();
foreach (WlanClient.WlanInterface wlanIface in client.Interfaces)
{
Wlan.WlanBssEntry[] wlanBssEntries = wlanIface.GetNetworkBssList();
foreach (Wlan.WlanBssEntry network in wlanBssEntries)
{
byte[] macAddr = network.dot11Bssid;
string tMac = "";
for (int i = 0; i < macAddr.Length; i++)
{
tMac += macAddr[i].ToString("x2").PadLeft(2, '0').ToUpper();
}
listView1.Items.Add(String.Format("{0} Signal: {1}% ", (GetStringForSSID(network.dot11Ssid)), network.linkQuality));
/*
Console.WriteLine("Signal: {0}%.", network.linkQuality);
Console.WriteLine("BSS Type: {0}.", network.dot11BssType);
Console.WriteLine("MAC: {0}.", tMac);
Console.WriteLine("");*/
}
}
client = null;
}
static string GetStringForSSID(Wlan.Dot11Ssid ssid)
{
return System.Text.Encoding.ASCII.GetString(ssid.SSID, 0, (int)ssid.SSIDLength);
}
It is a common misunderstanding that setting a local variable to null frees up any memory. As the variable goes out of scope when leaving the method anyway, the object = null; line doesn't make any sense.
Also in .NET you can not force memory to be freed. The garbage collection does its job whenever it thinks it's time.
Generally spoken, every time you execute the constructor Someclass() a new object is created. In plain C# the location in memory is irrelevant. All members will be initialized, either implicitly e.g. to null or by your own code. If you obtain the same results in each loop iteration that is because the initialization is the same as in the previous runs.
I have sample code to Show new form dialog:
private void button1_Click(object sender, EventArgs e)
{
(new Form2()).ShowDialog(this);
GC.Collect();
}
If the form has buttons, panels labels etc. then Dispose method of this form2 is executed but if I add toolStrip then the method Dispose isn't executed. Why Dispose is executed in these some cases ?
I readed if form is showing by ShowDialog then I should execute Dispose method, but why it works sometimes without it ?
Edit:
Collect method can be added after ShowDialog. This method is only for tests and is executed multiple times.
To check if method Dispose was executed I added breakpoint (in debug mode). Of course Dispose of example with ToolStrip is executed when program is ending.
OK I know how to proper implemented it, for me was intrested why GC can't clean up if on the form is toolStrip ?
The most simple code to show it is:
Example 1 - result - 100,
Example 2 - result > 0 (GC can clean up),
Example 3 - always 0.
Why example 2 and 3 are that different ?
private class Form2 : Form
{
public static int disposed = 0;
byte[] data;
private System.Windows.Forms.ToolStrip toolStrip11;
public Form2(bool addToolStrip)
{
data = new byte[100000];
this.Shown += (sender, e) => { this.Close(); };
this.Controls.Add(new Button());
if (addToolStrip)
{
this.toolStrip11 = new System.Windows.Forms.ToolStrip();
this.Controls.Add(this.toolStrip11);
}
}
protected override void Dispose(bool disposing)
{
++disposed;
base.Dispose(disposing);
}
}
private void ShowResult()
{
GC.Collect(); GC.Collect();
GC.WaitForFullGCComplete();
MessageBox.Show(Form2.disposed.ToString());
Form2.disposed = 0;
}
private void button1_Click(object sender, EventArgs e)
{
//proper
for (int i = 0; i < 100; ++i)
{
using(Form2 f = new Form2(true))
{
f.ShowDialog();
}
}
ShowResult();
//ok GC can clean - why yes ?
for (int i = 0; i < 100; ++i)
{
Form2 f = new Form2(false);
f.ShowDialog();
}
ShowResult();
//GC can't clean - why not ?
for (int i = 0; i < 100;a ++i)
{
Form2 f = new Form2(true);
f.ShowDialog();
}
ShowResult();
}
Your code doesn't make sense - you're forcing a garbage collection cycle before making a call that you claim leaks memory.
You should only implement Dispose to get rid of unmanaged resources. For managed resources it simply doesn't make sense to try to beat the garbage collector in any common use case. Also, I'm not sure how you even think you're "leaking memory" since you cannot possibly predict or know when the GC has done its job properly - strictly speaking a .NET program only has a real memory leak if the GC refuses to clean it up properly. Since that is information undisclosed to the developer - even an explicit cleanup can, at its own discretion, elect not to collect all potential garbage - your claim is by definition unverifiable.
Many objects that are disposable also have a finalizer. When an object would normally be cleaned up by the GC it will first check if it has a finalizer that hasn't been run. If it hasn't, it goes into a queue for its finalizers to be run, and is then actually eligible for garbage collection.
Because of this mechanism it's possible for some disposable objects to have their resources cleaned up even if they are not explicitly disposed.
That said, this is an unreliable mechanism. You don't know when an object will be collected once it is eligible for collection, and the state that the object is in when the finalizer is run can result in unusual and undefined interactions with "itself".
You should avoid relying on a finalizer whenever possible and instead explicitly dispose of such disposable resources.
In general it's good practice to garbage collect as it frees up resources.
How do I implement correct garbage collection in the following code's keepingTime method? Or, in fact, do I even need to?!
System.Timers.Timer allows IDisposable interface so 'using' is an option, but not in the following as the scope of the timer needs to extend to the method myTimer_Elapsed that is subscribed to the Timer Elapsed event. I've made two attempts to garbage collect but both fail as the timer does not then hang around long enough!
I've previously dicussed this code, for other reasons, in HERE
public partial class AirportParking : Form
{
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//instance variables of the form
System.Timers.Timer myTimer;
private const string EvenText = "hello";
private const string OddText = "hello world";
static int tickLength = 100;
static int elapsedCounter;
private int MaxTime = 5000;
private TimeSpan elapsedTime;
private readonly DateTime startTime = DateTime.Now;
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public AirportParking()
{
InitializeComponent();
lblValue.Text = EvenText;
keepingTime();
}
//method for keeping time
public void keepingTime() {
myTimer = new System.Timers.Timer(tickLength);
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
//ATTEMPT_1.tried the following unsuccessfully
//using (System.Timers.Timer myTimer = new System.Timers.Timer(tickLength))
//{
// myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
// myTimer.AutoReset = true;
// myTimer.Enabled = true;
// myTimer.Start();
//}
//ATTEMPT_2.tried the following unsuccessfully
//myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
//myTimer.AutoReset = true;
//myTimer.Enabled = true;
//try
//{
// myTimer.Start();
//}
//finally
//{
// myTimer.Dispose();
//}
}
private void myTimer_Elapsed(Object myObject,EventArgs myEventArgs){
elapsedCounter++;
elapsedTime = DateTime.Now.Subtract(startTime);
if (elapsedTime.TotalMilliseconds < MaxTime)
{
this.BeginInvoke(new MethodInvoker(delegate
{
this.lblElapsedTime.Text = elapsedTime.ToString();
if (elapsedCounter % 2 == 0)
this.lblValue.Text = EvenText;
else
this.lblValue.Text = OddText;
}));
}
else {myTimer.Stop();}
}
}
The only place you'll want to dispose of the timer resource is in the callback function myTimer_Elapsed. Where you do myTime.Stop(); you can also do myTimer.Dispose();.
However, when this part of the application goes out of scope, all these variables will be cleaned up anyway. As long as the timer is eventually stopped, once it's dereferenced it will be collected by the GC.
The reason the using blocks (and your current Dispose()) doesn't work is that you're throwing away the timer as soon as you create it! You have to let it run in the background.
Dispose your timer on Form's OnClosing event, if this is not a main form, or by the way, a Form that always visible.
A pseudocode can look like this:
public partial class AirportParking : Form
{
.....
.....
protected override void OnClosing(...)
{
myTimer.Dispose();
}
}
If this is some "long-running" form, you should add disposal of the timere at the moment you no more need it, but I think you already know that.
Although it is good practice to garbage collect, in this instance it would be would be done automatically when you close the form or the instance dies.
The only time I really worry about this is with SQL readers as you have to make sure they are closed off when you are finished otherwise it causes all sorts of problems.
In my current project there is a Form class which looks like this:
public partial class FormMain : Form
{
System.Timers.Timer timer;
Point previousLocation;
double distance;
public FormMain()
{
InitializeComponent();
distance = 0;
timer = new System.Timers.Timer(50);
timer.AutoReset = true;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (previousLocation != null)
{
// some code
UpdateDistanceLabel(distance);
UpdateSpeedLabel(v);
}
previousLocation = Cursor.Position;
}
private void UpdateDistanceLabel(double newDistance)
{
if (!lblDistance.IsDisposed && !IsDisposed)
{
Invoke(new Action(() => lblDistance.Text = String.Format("Distance: {0} pixels", newDistance)));
}
}
private void UpdateSpeedLabel(double newSpeed)
{
if (!lblSpeed.IsDisposed && !IsDisposed)
{
Invoke(new Action(() => lblSpeed.Text = String.Format("Speed: {0} pixels per second", newSpeed)));
}
}
}
As you can see, I am using a System.Timers.Timer object. I know I could use System.Windows.Forms.Timer, but I'm fairly interested in the reason why I'm still getting the exception shown in the title. It gets thrown at the Invoke call in the UpdateDistanceLabel method. What confuses me is that it says "Cannot access disposed object: FormMain" even though I am checking whether it is disposed or not. So that shouldn't happen. I have also tried disposing the timer object in the FormClosing event as well as overriding Dispose(bool) and disposing it there, both of which unfortunately didn't help at all. Also, the exception does not always get thrown, supposedly only when the timer happens to fire whilst the program is exiting. It still happens a lot.
I've seen that there are tons of threads about this, but I've already tried the solutions posted there, most of them involve checking the IsDisposed property - which doesn't work for me. So I guess I am doing something wrong.
So my question:
Why does the code posted above fire an exception even though I am checking whether the objects I am accessing are disposed or not?
There are two workarounds: either swallow the exception and curse Microsoft for not having included a TryInvoke and TryBeginInvoke methods, or else use locking to ensure that no attempt is made to Dispose the object while it's in use, and no attempt is made to use the object while Dispose is in progress. I think swallowing the exception is probably better, but some people have a visceral reaction against such things, and using locking it is possible to avoid having the exception occur in the first place.
One problem is that you are doing the check on the timer thread, before calling Invoke. There is a possible race condition, where the Form can be disposed after your check and before the invoked action is executed.
You should be doing the check inside the method (lambda expression in your case) called by Invoke.
Another possible problem is that you're accessing Cursor.Position on the timer thread. I'm not sure if this is valid - I'd do this on the main thread. Your code also includes the comment //some code - so you've presumably omitted some code that you also need to check.
Overall, you'd probably be better using a System.Windows.Forms.Timer.
Here is my solution to your exception if you are interested:
private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
timer.Stop();
Application.DoEvents();
}
.Stop() without .DoEvents() is not enough, as it'll dispose objects without waiting for your thread to finish its work.
Create two booleans called 'StopTimer' and 'TimerStopped' with their initial states set to false. Set the timer's AutoReset property to false. Then format the Elapsed method to the following:
Invoke((MethodInvoker)delegate {
// Work to do here.
});
if (!StopTimer)
timer.Start();
else
TimerStopped = true;
This way you are preventing a race condition, checking if the timer should continue and reporting when the method has reached its end.
Now set your FormClosing method to this:
if (!TimerStopped)
{
StopTimer = true;
Thread waiter = new Thread(new ThreadStart(delegate {
while (!TimerStopped) { }
Invoke((MethodInvoker)delegate { Close(); });
}));
waiter.Start();
e.Cancel = true;
}
else
timer.Dispose();
If the timer hasn't stopped yet, a thread is launched to wait until it has done so and then try to close the form again.