Click here to Skip to main content
16,004,901 members
Home / Discussions / C#
   

C#

 
GeneralRe: Dispose Method Pin
Luc Pattyn13-Aug-07 1:53
sitebuilderLuc Pattyn13-Aug-07 1:53 
GeneralRe: Dispose Method Pin
Martin#13-Aug-07 2:15
Martin#13-Aug-07 2:15 
GeneralRe: Dispose Method Pin
Guffa30-Jul-07 22:09
Guffa30-Jul-07 22:09 
GeneralRe: Dispose Method Pin
Luc Pattyn30-Jul-07 22:49
sitebuilderLuc Pattyn30-Jul-07 22:49 
GeneralRe: Dispose Method Pin
Guffa2-Aug-07 7:47
Guffa2-Aug-07 7:47 
GeneralRe: Dispose Method Pin
Luc Pattyn2-Aug-07 8:23
sitebuilderLuc Pattyn2-Aug-07 8:23 
GeneralRe: Dispose Method Pin
Guffa4-Aug-07 1:08
Guffa4-Aug-07 1:08 
GeneralRe: Dispose Method [modified] Pin
Luc Pattyn4-Aug-07 14:48
sitebuilderLuc Pattyn4-Aug-07 14:48 
Hi Guffa,

here is, I think, the verdict on our GC disagreement:

BTW: I was unable to see disassembly, CTRL/ALT/D is not available in C# Express Edition
(it is in C# Pro, and in all C++ tho). See "How to: Use the Disassembly Window"
on MSDN.
Any suggestion (except the obvious) is welcome.

I studied your program in detail;
in debug, it keeps all the arrays (but not on purpose, as you implied; but rather
because the MSIL did not get optimized, as we will see later)
in release, it throws away everything immediately

The big difference between the two is apparent in the MSIL:
debug generates a call to Create(), then an stloc to save the pointer in a local
(there are 4 numbered ones, the rest is on stack);
in my theory the GC sees these pointers till the method Main() is done.
release generates same call, then a pop, since the arrays are never used,
there is no need to keep the pointers; hence the GC in my theory never sees the arrays.


So I came up with a slightly different test program.
It has two test cases, one very similar to yours,
one more realistic in that the arrays are really used within the method,
so they get stored.
I used smaller arrays but included explicit GC calls, and one explicit ptr=null statement.

Results:
- debug test case 1 keeps everything alive
- release test case 1 does not store a single pointer, so everything is gone
- test case 2 behaves identical in debug and release, it acts according to my
theory: objects get collected if they have no references on stack, either because
the stack unwinds (end of method), or they get nulled.

So I maintain GC does not look at code, it scans the stacks.
And nulling a reference is useful if:
- the ref is somehow global;
- or the ref is local but followed by more code (hence no stack unwinding imminent)

class Program {

	static List<WeakReference> refs;

	static void Main(string[] args) {
		Console.WriteLine("===========================================");
		withoutConsumers();
		Console.WriteLine("===========================================");
		withConsumers();
		Console.WriteLine("===========================================");
		Console.WriteLine("Hit ENTER to terminate");
		Console.ReadLine();
	}

	// test case #1
	static void withoutConsumers() {
		Console.WriteLine("Arrays are created but never really used");
		refs = new List<WeakReference>();
		byte[] ref1, ref2, ref3;

		ref1 = Create_1MB();
		ref2 = Create_1MB();
		ref3 = Create_1MB();
		Show("after creates");
		Collect();
		Show("after collect");
		ref2=null;
		Show("after cref2=null");
		Collect();
		Show("after collect");
	}

	// test case #2
	static void withConsumers() {
		Console.WriteLine("This time arrays are really used");
		refs = new List<WeakReference>();
		byte[] ref1, ref2, ref3;

		ref1 = Create_1MB();
		ref2 = Create_1MB();
		ref3 = Create_1MB();
		Show("after creates");
		int n=ref1[1]+ref2[2]+ref3[3];
		Console.WriteLine("n="+n);
		Collect();
		Show("after collect");
		ref2=null;
		Show("after cref2=null");
		Collect();
		Show("after collect");
	}

	// create a 1MB array
	static byte[] Create_1MB() {
		byte[] r = new byte[1024*1024];
		for(int i=0; i<100; i++) r[i]=(byte)i;
		refs.Add(new WeakReference(r));
		return r;
	}

	// show alive state for 3 arrays
	static void Show(string title) {
		string s=title.PadRight(20);
		foreach(WeakReference r in refs) s+=" "+r.IsAlive;
		Console.WriteLine(s);
	}

	// try to collect everything, all generations, ...
	static void Collect() {
		GC.Collect();
		GC.Collect();
		GC.Collect();
	}
}


This is the output it generates in debug mode:

Arrays are created but never really used
after creates        True True True
after collect        True True True
after cref2=null     True True True
after collect        True False True
===========================================
This time arrays are really used
after creates        True True True
n=6
after collect        True True True
after cref2=null     True True True
after collect        True False True


This is the output it generates in release mode:

Arrays are created but never really used
after creates        False False True
after collect        False False False
after cref2=null     False False False
after collect        False False False
===========================================
This time arrays are really used
after creates        True True True
n=6
after collect        True True True
after cref2=null     True True True
after collect        True False True


Hope this convincingly illustrates my theory.

[Added: my interest in this got triggered by the idea that ptr=null; without
further consumers for ptr should and would be optimized away by a good compiler;
turns out that MSIL has a ldc.i4.0 to load a 4-byte integer zero, and
a ldnull instruction to push a null reference.
So from this the JIT knows it could eliminate a "ldc.i4.0 stloc" sequence
if the target of the stloc is not alive (life analysis by JIT, not by GC),
but more importantly it will have a built-in rule preventing it from
eliminating the sequence "ldnull stloc". So my mystery is solved.]

Smile | :)



-- modified at 20:55 Saturday 4th August, 2007


GeneralRe: Dispose Method Pin
Luc Pattyn2-Aug-07 21:56
sitebuilderLuc Pattyn2-Aug-07 21:56 
GeneralRe: Dispose Method Pin
Guffa4-Aug-07 1:19
Guffa4-Aug-07 1:19 
AnswerRe: Dispose Method Pin
Karthi_jpk30-Jul-07 3:19
Karthi_jpk30-Jul-07 3:19 
AnswerRe: Dispose Method Pin
Scott Dorman31-Jul-07 14:29
professionalScott Dorman31-Jul-07 14:29 
GeneralRe: Dispose Method Pin
N a v a n e e t h31-Jul-07 18:21
N a v a n e e t h31-Jul-07 18:21 
GeneralRe: Dispose Method Pin
Scott Dorman31-Jul-07 19:43
professionalScott Dorman31-Jul-07 19:43 
QuestionConnecting to SQL Server through C#.net Pin
Saba0230-Jul-07 2:32
Saba0230-Jul-07 2:32 
AnswerRe: Connecting to SQL Server through C#.net Pin
originSH30-Jul-07 3:03
originSH30-Jul-07 3:03 
AnswerRe: Connecting to SQL Server through C#.net Pin
Karthi_jpk30-Jul-07 3:04
Karthi_jpk30-Jul-07 3:04 
GeneralRe: Connecting to SQL Server through C#.net [modified] Pin
originSH30-Jul-07 3:19
originSH30-Jul-07 3:19 
QuestionAnother instance of my program is already running? Pin
pmartike30-Jul-07 2:17
pmartike30-Jul-07 2:17 
AnswerRe: Another instance of my program is already running? [modified] Pin
Martin#30-Jul-07 2:22
Martin#30-Jul-07 2:22 
QuestionSMO Impersonation? Pin
NanaAM30-Jul-07 1:47
NanaAM30-Jul-07 1:47 
QuestionSir Who redirect Fonts from C:\WINDOWS\Fonts to my Sample Application ? Pin
CodeVarma30-Jul-07 1:37
CodeVarma30-Jul-07 1:37 
QuestionConverting numbers and run some math equations in C# Pin
guyav9930-Jul-07 1:36
guyav9930-Jul-07 1:36 
AnswerRe: Converting numbers and run some math equations in C# Pin
User 665830-Jul-07 1:49
User 665830-Jul-07 1:49 
GeneralRe: Converting numbers and run some math equations in C# Pin
guyav9930-Jul-07 1:51
guyav9930-Jul-07 1:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.