Dispose(bool), Idisposable, form closing etc.

  • Thread starter Thread starter rbrowning1958
  • Start date Start date
[snip]

However, you're right that parameters are treated differently from
local variables, and as long as the parameter references the instance
for the duration of the call to the method, the instance won't be
collected.

[snip]

I don't know if I'm convinced of that. You may have been right the
first time. What made you change your mind? Is there a section in
the CLI specification that talks about this?

I wrote a test myself. And just like Michael said, while I could
easily reproduce early collection for an object referenced by a local
variable, I could not get it to happen with an object referenced only
by a parameter.

Granted, it's not really a scientifically valid proof. Lack of
behavior isn't proof. But it's pretty compelling.

It is possible that the CLI _allows_ for early collection of
parameter-held references, and that the current implementation itself
just doesn't take advantage of that. But if so, the behavior is (I
think) more important than the theoretical possibility in this case,
since we're talking about what Application.Run() _does_, not what it
might do.
Obviously, a parameter can be collected during the execution of an
unmanaged function.

Based on what I've seen, I'm not convinced of that. Even when there's
no actual use of the parameter in a method, the object referenced by it
is apparently not collectable until the method returns. It wouldn't
matter whether you called a managed or unmanaged function.
That's the most important reason for the
existence of GC.KeepAlive.

I'm not sure I'd rank the reasons in that way, but I'm not willing to
quibble about which is the _most_ important. Clearly you need
GC.KeepAlive() any time you need to avoid early collection of
references that aren't explicitly used later in the method. There are
lots of different ways to run into the issue, and calling an unmanaged
function is just one example.

But if you want that to be the most important, that's okay by me. :)
But, I want to know if a parameter can be
collected before the completion of a managed function. I haven't seen
conclusive documentation that guarentees that it won't. In fact, I
don't see any reason why the runtime couldn't support that
optimization. Comments?

I'm not aware of any guarantee that it won't either. I can just say
that I tried very hard to get it to happen and was unable to.

Pete
 
Dispose does not delete the instance. It only frees, what should not wait
for the GC to be freed. The fields of the instance still live on, until the
instance is collected. So any propertie, that does not depend on the 'open
state' of the form will be accessible. This includes all properties that
simply access fields. But, in case of a form, you can't access f.e. the
controls of the form (or at least, you shouldn't count on).
If the form is destroyed in the sense, that it is collected, than there is
no reference to the instance left (if the GC worked right ;-) ), and so no
member of the instance can be accessed.

Christof

Hi Cristof,

What confused me - and is clear now - is that Dispose(bool) will
destroy the components / controls, but as someone else wrote if you
have their important properties stored in simple instance variables
then you can still access those until the form is destroyed (i.e. the
garbage collector has kicked in and freed the object's memory).

Cheers

Ray
 
I used the word canonical to mean an authorized, well-established
pattern.










I don't think I was very clear. What I meant was that you may still
be able to call some property getters without them throwing exceptions
even after Dispose has been called on the object. That's because
property getters are usually nothing more than trivial wrappers around
instance variables.- Hide quoted text -

- Show quoted text -

Clear - thanks. I was confused Re the values in the components which
would be destroyed by dispose(bool) versus simple instance vars which
are freed when the form is garbage collected. We tend to use the word
destroy for both things which is a bit confusing.

Ta

Ray
 
[snip]
However, you're right that parameters are treated differently from
local variables, and as long as the parameter references the instance
for the duration of the call to the method, the instance won't be
collected.
[snip]
I don't know if I'm convinced of that. You may have been right the
first time. What made you change your mind? Is there a section in
the CLI specification that talks about this?

I wrote a test myself. And just like Michael said, while I could
easily reproduce early collection for an object referenced by a local
variable, I could not get it to happen with an object referenced only
by a parameter.

Granted, it's not really a scientifically valid proof. Lack of
behavior isn't proof. But it's pretty compelling.

It is possible that the CLI _allows_ for early collection of
parameter-held references, and that the current implementation itself
just doesn't take advantage of that. But if so, the behavior is (I
think) more important than the theoretical possibility in this case,
since we're talking about what Application.Run() _does_, not what it
might do.
Obviously, a parameter can be collected during the execution of an
unmanaged function.

Based on what I've seen, I'm not convinced of that. Even when there's
no actual use of the parameter in a method, the object referenced by it
is apparently not collectable until the method returns. It wouldn't
matter whether you called a managed or unmanaged function.
That's the most important reason for the
existence of GC.KeepAlive.

I'm not sure I'd rank the reasons in that way, but I'm not willing to
quibble about which is the _most_ important. Clearly you need
GC.KeepAlive() any time you need to avoid early collection of
references that aren't explicitly used later in the method. There are
lots of different ways to run into the issue, and calling an unmanaged
function is just one example.

But if you want that to be the most important, that's okay by me. :)
But, I want to know if a parameter can be
collected before the completion of a managed function. I haven't seen
conclusive documentation that guarentees that it won't. In fact, I
don't see any reason why the runtime couldn't support that
optimization. Comments?

I'm not aware of any guarantee that it won't either. I can just say
that I tried very hard to get it to happen and was unable to.

Pete

Pete and Brian,

I'm not familiar with the difference between a weak and a strong
reference. The way my simple mind works would lead me to believe that
a form's
constructor adds itself to the application.openForms list, and the
dispose(bool) thing would remove it from that list, making it eligible
for garbage collection as long as nothing else references the form.
Since application.run is instantiating a form then its constructor
adds it to applcation,openforms thereby keeping it alive even if there
is no other variable referencing it. Am I missing something?

Ta

Ray
 
It is possible that the CLI _allows_ for early collection of
parameter-held references, and that the current implementation itself
just doesn't take advantage of that. But if so, the behavior is (I
think) more important than the theoretical possibility in this case,
since we're talking about what Application.Run() _does_, not what it
might do.

Maybe. I'm just thinking about theoretical possibilities that I could
not demonstrate in 1.1, but were a piece of cake in 2.0. At some
level you have to consider the possibility or risk hard to find bugs
when your code runs on a newer version of the framework.
Based on what I've seen, I'm not convinced of that. Even when there's
no actual use of the parameter in a method, the object referenced by it
is apparently not collectable until the method returns. It wouldn't
matter whether you called a managed or unmanaged function.

Yeah, after some more thought I think you're right. The interop
marshaler would likely prevent the reference from being eligible until
the function returns. Asynchronous functions and functions that
accept say...an IntPtr to a managed object would still be problematic.
I'm not sure I'd rank the reasons in that way, but I'm not willing to
quibble about which is the _most_ important. Clearly you need
GC.KeepAlive() any time you need to avoid early collection of
references that aren't explicitly used later in the method. There are
lots of different ways to run into the issue, and calling an unmanaged
function is just one example.

But if you want that to be the most important, that's okay by me. :)

No, you are right. I should be careful about ranking things like
that. There certainly are other reasons for it's use. A scenario
involving an unmanaged function just happened to be the only time I've
used it :)
 
I'm not familiar with the difference between a weak and a strong
reference.

You don't really need to be. As far as garbage collection is
concerned, a weak reference isn't a reference at all, while a strong
reference is just a normal reference like what you're accustomed to.
The way my simple mind works would lead me to believe that
a form's
constructor adds itself to the application.openForms list, and the
dispose(bool) thing would remove it from that list, making it eligible
for garbage collection as long as nothing else references the form.

That's not quite correct. A form is added to the open forms list when
it's shown, and removed when it's hidden. Simply constructing the form
doesn't add it, and it can be removed from the list without disposing
the form.
Since application.run is instantiating a form then its constructor
adds it to applcation,openforms thereby keeping it alive even if there
is no other variable referencing it. Am I missing something?

Application.Run() shows the form, and since it must maintaing the
reference itself until that point, you're assured the GC won't collect
the form before that point. After that point, the shown form is in the
open forms list and thus also won't be collected.

IMHO, the important thing here is that the system does work and forms
won't be collected prematurely, assuming you use the normal idioms in
..NET. You can, of course, manage your forms in some obfuscated way
that does manage to prematurely release a form, but you'd have to go
out of your way to do that (for example, showing the form outside the
normal Application.Run() mechanism, and storing the reference in a
WeakReference instance).

In any case, as long as the form is visible on the screen, it will not
be collected.

Pete
 
Brian Gideon said:
Yeah, after some more thought I think you're right. The interop
marshaler would likely prevent the reference from being eligible until
the function returns. Asynchronous functions and functions that
accept say...an IntPtr to a managed object would still be problematic.

I just tested this because I was pretty sure you were wrong but you are in
fact correct. When passing an instance of a class into an unmanaged function
I couldn't get it to dispose of the object while it was in the function.
Code is below if you want to test.

C++ code:
#include "stdafx.h"
#include "DelMeDll.h"

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

typedef int __stdcall SomeCallback(int Count);

// This is an example of an exported function.
int __stdcall DoIt(void* CallbackPtr, void* SomeClass)
{
SomeCallback* sc = (SomeCallback*)CallbackPtr;
for(int i = 0; i < 10; i++)
{
sc(i);
}
return 42;
}


C# code

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Runtime.InteropServices;

namespace DelMeCallDLL

{

public delegate int CallbackDelegate(int Count);

public class Form1 : System.Windows.Forms.Form

{

private System.Windows.Forms.Button button2;

private System.Windows.Forms.Button button1;


[DllImport("C:\\Stuff\\DelMeDll\\Debug\\DelMeDll.dll")]

static extern int DoIt(CallbackDelegate CallBack, SomeClass MyClass);

public Form1()

{

InitializeComponent();

}

#region Windows Form Designer generated code

private void InitializeComponent()

{

this.button1 = new System.Windows.Forms.Button();

this.button2 = new System.Windows.Forms.Button();

this.SuspendLayout();

//

// button1

//

this.button1.Location = new System.Drawing.Point(104, 80);

this.button1.Name = "button1";

this.button1.Size = new System.Drawing.Size(80, 80);

this.button1.TabIndex = 0;

this.button1.Text = "button1";

this.button1.Click += new System.EventHandler(this.button1_Click);

//

// button2

//

this.button2.Location = new System.Drawing.Point(192, 208);

this.button2.Name = "button2";

this.button2.Size = new System.Drawing.Size(64, 48);

this.button2.TabIndex = 1;

this.button2.Text = "button2";

this.button2.Click += new System.EventHandler(this.button2_Click);

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(292, 266);

this.Controls.Add(this.button2);

this.Controls.Add(this.button1);

this.Name = "Form1";

this.Text = "Form1";

this.ResumeLayout(false);

}

#endregion

[STAThread]

static void Main()

{

Application.Run(new Form1());

}

private void button1_Click(object sender, System.EventArgs e)

{

DoIt(new CallbackDelegate(this.Callback), new SomeClass());

}

private int Callback(int Count)

{

GC.Collect();

Console.WriteLine(Count.ToString());

return Count;

}

private void button2_Click(object sender, System.EventArgs e)

{

GC.Collect();

}

}

}
 
You don't really need to be. As far as garbage collection is
concerned, a weak reference isn't a reference at all, while a strong
reference is just a normal reference like what you're accustomed to.


That's not quite correct. A form is added to the open forms list when
it's shown, and removed when it's hidden. Simply constructing the form
doesn't add it, and it can be removed from the list without disposing
the form.


Application.Run() shows the form, and since it must maintaing the
reference itself until that point, you're assured the GC won't collect
the form before that point. After that point, the shown form is in the
open forms list and thus also won't be collected.

IMHO, the important thing here is that the system does work and forms
won't be collected prematurely, assuming you use the normal idioms in
.NET. You can, of course, manage your forms in some obfuscated way
that does manage to prematurely release a form, but you'd have to go
out of your way to do that (for example, showing the form outside the
normal Application.Run() mechanism, and storing the reference in a
WeakReference instance).

In any case, as long as the form is visible on the screen, it will not
be collected.

Pete

Pete,

<<That's not quite correct. A form is added to the open forms list
when
it's shown, and removed when it's hidden. Simply constructing the
form
doesn't add it, and it can be removed from the list without disposing
the form.>>
I don't think that's quite correct - but what do I know? What my tests
show is that the form is not removed from application.openForms when a
form is hidden. What's unusual though is it is not added to the
openForms list until it is shown. If you create the form but don't
display it it's not in the list. As soon as you show it it is added to
the list and not removed until the form is closed (close method or
clicking close button which I presume calls Dispose(bool)). I wonder
what happens when you do this:

Form2 form2;
form2 = new Form2();

i.e. declare and instantiate inside a routine. It's probably a
candidate for GC straight away - nothing references it and it's not
visible. It's not disposed(bool) though is it? If I did show it it
gets added to the application.forms list so there is a reference to
it.

So what's a "WeakReference instance"?

Best

Ray
 
No, I gave it a test with a regular class. The class would not be collected
until the function exited.




I wasn't talking about Application.Run at all. I was just using a regular
function that I defined myself and passed in an instance of a class I
defined. The only way I could get the class to be GCd before the function
exits was to set the parameter to null inside the function.


That's true although that doesn't mean it did exist either.

Michael

Michael,

What I've found is that showing a form adds itself to
application.openForms, but hiding a modeless form does not remove it.
Surely the reason this works with Application.run is because that
shows the form, hence its referenced by application.openForms. Hiding
a modeless form doess seem to remove it from application.openForms
which kind of makes sense because closing it does not dispose it
therefore it needs to be removed from application.forms so it can be a
candidate for GC.

Ray
 
<<That's not quite correct. A form is added to the open forms list
when
it's shown, and removed when it's hidden. Simply constructing the
form
doesn't add it, and it can be removed from the list without disposing
the form.>>
I don't think that's quite correct - but what do I know? What my tests
show is that the form is not removed from application.openForms when a
form is hidden.

I should have been more precise, especially given that you can hide a
form without closing it.

Closing a modal form doesn't dispose it; it just hides it. But doing
so is still sufficient for it to be removed from the open forms list.

But you're right, the important act is closing the form, not hiding it.
What's unusual though is it is not added to the
openForms list until it is shown. If you create the form but don't
display it it's not in the list. As soon as you show it it is added to
the list and not removed until the form is closed (close method or
clicking close button which I presume calls Dispose(bool)).

Right. Noting that for modal forms closing the form doesn't dispose it.
I wonder
what happens when you do this:

Form2 form2;
form2 = new Form2();

i.e. declare and instantiate inside a routine. It's probably a
candidate for GC straight away - nothing references it and it's not
visible.

That's correct.
It's not disposed(bool) though is it?

The finalizer will dispose it, eventually.
If I did show it it
gets added to the application.forms list so there is a reference to
it.

That's correct.
So what's a "WeakReference instance"?

WeakReference is a class. A WeakReference instance is an instance of
that class.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top