C# - ShowDialog(this) memory leak - c#

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.

Related

How to ensure garbage collection when user closes a non-modal window?

In my C# Winforms App, I have the following (minimal code shown)
Form1 is the main app that the user uses to do stuff. Form2 shows a help file that explains how to use the features on Form1 to do stuff. I want the user to be able to display (modeless) and close the help file at will so long as Form1 is visible.
I also worry about the memory leak that may occur as the user opens and closes Form2. So, when the user closes Form2, it raises an event that Form1 subscribes to. When the Form1 event method is invoked, is calls Dispose() on Form2, sets the Form2 object to null and calls the garbage collector.
Will this remove the chance for a memory leak caused by the user opening and closing Form2? Is it overkill? Is there a better way of ensuring garbage collection at the point in time that Form2 is closed? I don't want to rely on Windows doing it later when it decides to
UPDATE
Jimi pointed out that I don't need a custom event handler for the Form2 Closed event. He's right. In my Form1 class, I'm now using the standard FormClosedEventHandler for my Form2. The code itself, however remains pretty much the same. However, when I remove the call to GC.Collect(), I'm seeing evidence of a memory leak using Task Manager.
Here's my data:
Run #1. Form2_FormClosed method has:
------------------------------------
f_HelpForm.Dispose();
f_HelpForm = null;
GC.Collect();
Start App
Task Manager Memory for app: 6.7 MB
Open and Close Form2 20 times
Task Manager Memory for app: 8.2 MB
Run #2. Form2_FormClosed method has:
------------------------------------
f_HelpForm.Dispose();
f_HelpForm = null;
//GC.Collect();
Start App
Task Manager Memory for app: 6.9 MB
Open and Close Form2 20 times
Task Manager Memory for app: 18.9 MB
Run #3. Form2_FormClosed method has:
------------------------------------
//f_HelpForm.Dispose();
f_HelpForm = null;
//GC.Collect();
Start App
Task Manager Memory for app: 6.9 MB
Open and Close Form2 20 times
Task Manager Memory for app: 18.1 M
Without the call to GC.Collect(), and with or without the call to Dispose(), the footprint grows 100% bigger as compared to the code that includes the call to GC.collect().
I hear what you guys are saying, but .......... I think I'll leave my code in its "Run #1" configuration
The code
Note: I acknowledge that setting form2 = null has a direct influence on behind-the-scenes garbage collection. However, my purpose in setting form2 = null is to provide a signal to the Form2Button_Click method that it can use to decide whether or not to instantiate a Form2 object
public partial class Form1 : Form
{
Form2 form2;
public Form1()
{
form2 = null;
}
private void Form2Button_Click(object sender, EventArgs e)
{
if (form2 == null)
{
form2 = new ClsHelpForm(this);
form2.Form2Closed += Form2_FormClosed;
}
form2.Select();
form2.Show();
}
//When this user clicks the Help button on Form1, this method is invoked
private void Form2_FormClosed(object sender, EventArgs e)
{
form2.Form2Closed -= Form2_FormClosed;
form2.Dispose();
form2 = null;
GC.Collect();
}
{
public partial class Form2 : Form
{
public event EventHandler Form2Closed;
public Form2()
{
}
//When the user clicks the "X" button on Form2, this method is invoked
private void Form2_FormClosed(object sender, Form2EventArgs e)
{
Form2Closed?.Invoke(this, EventArgs.Empty);
}
}
I also worry about the memory leak that may occur as the user opens and closes Form2.
Why are you worried about a memory leak? Don't try to optimize when there's no evidence of a problem. As long as Form2 and all of its child objects actually clean up resources when Dispose is called, there should be no memory leak.
Is there a better way of ensuring garbage collection at the point in time that Form2 is closed? I don't want to rely on Windows doing it later when it decides to
This seems like unnecessary paranoia. Just be sure Form2 cleans up in its Dispose method and let garbage collection occur naturally. Everything looks fine except remove the GC.Collect() call.
A member instance of Form2 doesn't take a lot of room on the heap and there seems to be little cause to create and destroy its Handle every time the user wants to show it.
Why not just prevent the destruction of the Form2 handle until app closes?
public partial class Form2 : Form
{
public Form2(Form owner)
{
InitializeComponent();
Owner = owner;
StartPosition = FormStartPosition.Manual;
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if(Visible)
{
Location = new Point(
Owner.Location.X + Owner.Width + 10,
Owner.Location.Y);
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if(e.CloseReason.Equals(CloseReason.UserClosing))
{
e.Cancel = true;
Hide();
}
}
}
When the form2 is cycled (by automation) 100 times, monitoring the process memory shows zero GCs and no direct correlation to the number of times shown.
Where:
public partial class Form1 : Form
{
Form2 _form2;
public Form1()
{
InitializeComponent();
_form2 = new Form2(this);
Disposed += (sender, e) => _form2.Dispose();
buttonShowHelp.Click += async (sender, e) =>
{
for (int i = 0; i < numericUpDown.Value; i++)
{
_form2.Visible = true;
await Task.Delay(500);
_form2.Visible = false;
await Task.Delay(500);
}
// But leave it open after cycling.
_form2.Visible = true;
};
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
}
}
If you put trash in the kitchen garbage can, and 5 minutes later the trash is still there, it does NOT mean that the trash will be there forever. It just means that the trash hasn't been emptied yet. It will get emptied eventually once it gets full enough. A "leak" would be if you put your trash on the floor, in which case it would not get picked up when the trashcan gets full. (not a perfect analogy, since someone is (hopefully) going to pick it up at some point, but you get the idea)
Your observations probably show a 100% growth because there's no memory pressure to require a collection. A memory leak would only occur if that memory never gets freed by the GC, which forcing a garbage collection would not fix. An example of a memory leak would be a reference to an unmanaged resource that is not release when the form is disposed (not closed).
Calling GC.Collect yourself is not going to fix memory leaks - it just cleans up memory earlier that the system needs to.

What is spcial about IntPtr etc that means they can be touched in the finalizer?

In a standard dispose/finalise pattern such as Finalizers with Dispose() in C# the Dispose(bool) does not touch managed objects if the method is called from the finalizer, it is considered unsafe as they may have already been collected by the garbage collector.
What is special about IntPtr etc that makes them safe?
As some background, to keep the cleanup code near the allocate code I'm adding the cleanup action to an event as soon as I allocate, then calling the event from the dispose method:
class TestClass : IDisposable
{
private IntPtr buffer;
public void test()
{
buffer = Marshal.AllocHGlobal(1024);
OnFreeUnmanagedResource += (() => Marshal.FreeHGlobal(buffer));
}
private List<IDisposable> managedObjectsToBeDisposed = new List<IDisposable>();
private event Action OnFreeUnmanagedResource = delegate { };
private bool _isDisposed = false;
private void Dispose(bool itIsSafeToAlsoFreeManagedObjects)
{
if (_isDisposed) return;
OnFreeUnmanagedResource();
if (itIsSafeToAlsoFreeManagedObjects)
for (var i = managedObjectsToBeDisposed.Count - 1; i >= 0; i--)
{
var managedObjectToBeDisposed = managedObjectsToBeDisposed[i];
managedObjectToBeDisposed.Dispose();
managedObjectsToBeDisposed.Remove(managedObjectToBeDisposed);
}
_isDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~TestClass()
{
Dispose(false);
}
}
I'm uncertain of this code because OnFreeUnmanagedResource may be collected before the class, but why would this not be the case for buffer?
With that anti-pattern (really you're better off either only having managed fields or only an unmanaged field rather than mixing them and then having to be clever about how that mixing is dealt with, but alas the pattern is still with us and has to be dealt with sometimes) the danger is not that disposable managed objects may have been collected (they won't be, they're being kept alive by the field in the class in question, and now at least rooted from that object in the finalisation queue, if not elsewhere) but that they may have been finalised by their own finaliser. Or conversely, that they might get finalised here and then finalised again because they are already in the finalisation queue.
If you'd arrived at this code via Dispose() then they would not (assuming no bugs, obviously) have been cleaned up because they only path prior to a collection attempt that can clean them is that very method.
If you'd arrived at this code via the finaliser then this object has had collection attempted on it, and been put in the finalisation queue, which means that likely the objects accessible only through it also had collection attempted on it, and if finalisable had been put in the queue, and there's no guarantee of which was put there first.
If the object was disposable but not finalisable, it quite likely had fields in turn that were finalisable and likewise are likely to be on that queue.
And if the object was disposable but not finalisable, and had no finalisable fields then it doesn't matter that you don't do anything with it.
This would be OK:
private void Dispose(bool itIsSafeToAlsoFreeManagedObjects)
{
if (_isDisposed) return;
Marshal.FreeHGlobal(buffer));
if (itIsSafeToAlsoFreeManagedObjects)
for (var i = managedObjectsToBeDisposed.Count - 1; i >= 0; i--)
{
var managedObjectToBeDisposed = managedObjectsToBeDisposed[i];
managedObjectToBeDisposed.Dispose();
managedObjectsToBeDisposed.Remove(managedObjectToBeDisposed);
}
_isDisposed = true;
}
An IntPtr is a struct, that essentially contains a native handle. The resource refered to by the handle is not touched by the garbage collector. The struct itself is also still valid.
I'm not so sure about your code, where you use a managed object of reference type (the delegate attached to OnFreeUnmanagedResource) in the finalizer.
Edit: After reading the other answer, I think, your code is also OK, as the delegate doesn't have a finalizer.

How do C# threads cause memory leaks when they already finished?

Here's my code:
using System;
using System.Collections.Generic;
using System.Threading;
namespace testt
{
class MainClass
{
public static List<TestObject> testjobs = new List<TestObject> ();
public static void Main (string[] args)
{
Console.WriteLine ("Hello World!");
addTask (); //put a breakpoint here
Thread.Sleep (5000);
deleteObj ();
while (true) {
Console.WriteLine ("MAIN STILL EXISTS!");
Thread.Sleep (1500);
GC.Collect ();
}
}
public static void addTask()
{
for (int i = 0; i < 10; i++)
{
testjobs.Add (new TestObject ());
testjobs [i].Start ();
}
}
public static void deleteObj()
{
for(int i=0; i<10;i++)
{
testjobs [0].dispose ();
testjobs.RemoveAt (0);
}
Console.WriteLine (testjobs.Count);
}
}
public class TestObject
{
private bool _isStopRequested;
private Thread _thread;
public void Start()
{
_thread = new Thread(ThreadRoutine);
_thread.Start();
}
public void Stop()
{
_isStopRequested = true;
if(!_thread.Join(5000))
{
_thread.Abort();
}
}
public void dispose(){
this.Stop ();
this._thread.Abort ();
this._thread=null;
}
private void ThreadRoutine()
{
//while(!_isStopRequested) //THIS CAUSES THE MEMORY LEAK!!!
{
Thread.Sleep (1);
}
Console.WriteLine ("THREAD FINISHED");
}
~TestObject(){
Console.WriteLine ("===================TESTOBJECT DESTROYED!!===============");
}
}
}
If you run it with the //while(!_isStopRequested) uncommented, the TestObject instances will not be destroyed, i.e their destructor methods will not be called.
If you run it as is, then only 4-8 objects will be destroyed, not all 10 of them.
Why does this happen when the threads have fully exited? I checked with the Xamarin debugger and the threads were definitely stopped. If you put a breakpoint in Xamarin at addTask(); then you can see that 10 threads
My only explanation for this is that the thread somehow holds a reference back to their parent object TestObject instance even after they have finished. How can a thread hold a reference to their parent object when the thread has already finished?
Also, if I change Thread.Sleep(1) to Thread.Sleep(5000), the TestObjects also stop being collected.
Also, as it is, only some TestObjects get collected whilst others don't.
Why do these things happen? How can I ensure that ALL the TestObjects get garbage collected by the time the deleteObj() function returns?
EDIT: I just tested the exact same code in Visual Studio (.NET) and all of the objects were garbage collected regardless of whether if that line was commented out or not.
Therefore I now consider this issue to be a Mono-specific problem and there was no memory leak to begin with.
Finalizers are not deterministic. You cannot rely on them being called.
If it is vitally important for your program to clean up the resource in question then you should be explicitly disposing of it, and not relying on a finalizer.
If cleaning up the resource would be nice, but you don't really care all that much if the finializer gets to it or not, then you can choose to not explicitly dispose of the unmanaged resources.
Also note that making a managed object eligible for garbage collection doesn't necessarily mean that it will be garbage collected. It means it can be collected whenever the collector feels like it.
Finally, recognize that aborting a thread is another unreliable thing to do. The thread do. There are a number of ways for a thread that has been requested to abort will not successfully do so, or where it will cause any number of different types of problems when it does. You should avoid using Thread.Abort unless the thread in question was designed to be aborted, and you have a strong understanding of all of the many possible pitfalls of trying to reason about a program that could throw an exception between any two operations.

GC.WaitForPendingFinalizers not working in async method?

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).

problem disposing class in Dictionary it is Still in the heap memory although using GC.Collect

i have a problem disposing class in Dictionary
this is my code
private Dictionary<string, MyProcessor> Processors = new Dictionary<string, MyProcessor>();
private void button1_Click(object sender, EventArgs e)
{
if (!Processors.ContainsKey(textBox1.Text))
{
Processors.Add(textBox1.Text, new MyProcessor());
}
}
private void button2_Click(object sender, EventArgs e)
{
MyProcessor currnt_processor = Processors[textBox2.Text];
Processors.Remove(textBox2.Text);
currnt_processor.Dispose();
currnt_processor = null;
GC.Collect();
GC.WaitForFullGCComplete();
}
public class MyProcessor: IDisposable
{
private bool isDisposed = false;
string x = "";
public MyProcessor()
{
for (int i = 0; i < 20000; i++)
{
//this line only to increase the memory usage to know if the class is dispose or not
x = x + "gggggggggggg";
}
}
public void Dispose()
{
x=null;
this.Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (!this.isDisposed)
{
isDisposed = true;
this.Dispose();
}
}
~MyProcessor()
{
Dispose(false);
}
}
i use "ANTS Memory Profiler" to monitor heap memory
the disposing work only when i remove all keys from dictionary
how can i destroy the class from heap memory ?
this a video link for the problem
http://www.youtube.com/watch?v=ePorlksv2QY
thanks in advance
I think you are seeing ghosts - keep in mind that the .NET garbage collection is a generational garbage collection based on memory pressure. If there is no memory pressure your resources will not be garbage collected. Also calling GC.Collect() is just a bad idea, I hope you are only doing this for your profiling tests.
As an aside what resources exactly are your disposing in your Dispose method? Doesn't look like you need one.
In the implementation you provided not a single Dispose() method call nor the finalizer ~MyProcessor() is needed at all.
I think you might want to call a GC.WaitForFullGCComplete(); as GC.Collect(); just kicks off the GC.
It is possible to have a reference leak (ie you inadvertently keep a whole load of references to objects in a list somewhere, preventing their collection). I doubt that the dictionary has such a "leak" as it is such a widely used class that it would be a known problem. Most likely if you have problems with your code most likely the problem is somewhere else in your code.
If this is not causing you actual problems, lay off it, grab your towel and don't panic, the GC does work :)
You are removing item with one name but disposing item with another name from dictionary. So item that disposed is still referenced from dictionary (Processors.Remove(textBox2.Text); while current = dict[textBox1.Text]).
Take a note of Eric's comment - Dispose does not make object ready for garbage collection, if it has references it still will be in the memory.

Categories