A strange problem occurred during garbage collection.

Source: Internet
Author: User

I saw a post in the Forum a few days ago.Garbage collection-is it a bug?The problem is as follows:

Static tc gto;
Public class TC
{
Public int A = 99;
~ TC ()
{
A =-1; // set breakpoint 1
GTO = this;
}
}
Private void button#click (Object sender, eventargs E)
{
Tc to = new TC ();
GC. Collect ();
GC. waitforpendingfinalizers ();
Return; // set breakpoint 2
}
Two problems:
Click button once, it seem ~ TC () not execute at all. And it will excute at the second time clicking. Why?
The second clicking, breakpoint 2, "GTO" and "to", Gto. A =-1, while to. A = 99, why?

The answer is very good. The local variable to cannot be recycled during the first forced recycle. Only the TC object generated last time will be recycled at the next click ~ TC () is not executed immediately. The second problem is that in the destructor, object A is set to-1 and the reference is assigned to a global object, so GTO. A =-1, and the new TC object A is 99. I was confident in answering the first question, but when I was about to answer the question, I sawGomokuHe said:

1. During debugging, Visual Studio provides the function of displaying real-time values, for example, allowing you to check the TC value in breakpoint 2.
To check the TC value, TC cannot be recycled. Therefore, the Visual Studio debugger inserts special code to ensure that TC will not be recycled before leaving button#click.
2. The release version is different. If you compile and run the example using release, you will find that, unlike debug ,~ TC () was called at the first GC. Collect.

I immediately started the experiment and found that, as gomoku said, I had doubts about my understanding. Because I believe this is definitely irrelevant to the breakpoint of Vs, and msdn ,. net books and technical articles at Home and Abroad explain the principles of garbage collection in this way. "At the beginning, GC regarded all objects on the hosting stack as recyclable, then, the system looks for reachable objects from the root of an application. Inaccessible objects are recycled. The root pointer list is maintained by the JIT compiler and CLR runtime, it mainly includes global variables, static variables, local variables, and register pointers. "So I think this is the same as debug even in release, this local variable is not saved unless there is an optimization operation in the release. So I will check the versions of debug and release.

Debug:

Il_0000: NOP
Il_0001: newobj TC:. ctor
Il_0006: stloc.0
Il_0007: Call system. GC: Collect
Il_000c: NOP
Il_000d: Call system. GC: waitforpendingfinalizers
Il_0012: NOP
Il_0013: Br. s il_0015
Il_0015: Ret

Release:

Iladdr = 01394590
Il_0000: newobj TC:. ctor
Il_0005: Pop
Il_0006: Call system. GC: Collect
Il_000b: Call system. GC: waitforpendingfinalizers
Il_0010: Ret

We can see that there is a significant difference between debug and release. debug has the il_0006: stloc.0 command, while release directly pops after downloading the new TC. OK, with the Il base, you only need to check the jitted machine assembly to confirm that what is said on msdn is true, ...... A depressing thing happened. From the perspective of Vs, there is basically no difference between the disassembly of debug and release, and the local variable to value is saved on the stack. After thinking about it for half a day, Dawu gomoku's reply said that vs added some code to the disassembly for debugging convenience, so the debug and release I saw are the same.

The next thing is simple. It is a process of collecting evidence. However, even more depressing things happen. I spent two days figuring out how windbg got the jitted post-compilation method under release (truth is really hard to be verified, to prove a truth, you need to pay several times or even dozens of times more to expose lies and mistakes, of course, I'm not saying that gomoku is talking about errors, but it's just not about the key points of the problem.) here I 'd like to thank lbqArticleAndCiciMy guidance.

Main disassembly in debug:

MoV dword ptr [ebp-10h], eax // address of the new object
MoV ECx, dword ptr [ebp-10h]
Call dword ptr ds: [806de8h] (test_winform.gc_test + Tc .. ctor (), mdtoken: 0600004f)
MoV eax, dword ptr [ebp-10h]
MoV dword ptr [ebp-0Ch], eax // save object address on Stack
Call mscorlib_ni + 0x6b7bcc (6c6a7bcc) (system. gc. Collect (), mdtoken: 06000ab8) // force garbage collection
NOP
Call mscorwks! Gcinterface: runfinalizers (6cbe3491)

Release disassembly:

MoV EBP, ESP
MoV ECx, 7d6714h (MT: test_winform.gc_test + Tc)
Call mscorwks! Jit_newfast (6cafc5d4) // new TC ()
MoV dword ptr [eax + 4], 63 h // copy 99 to the object address + 4 (here is the location of a), which is equivalent to the constructor
XOR edX, EDX
Lea ECx, [edx-1]
Call mscorwks! Gcinterface: collectgeneration (6cbbd082)
Call mscorwks! Gcinterface: runfinalizers (6cbe3491)
Pop EBP
RET 4

Note that, although the TC object address is saved in eax before GC is forcibly called, however, because the register eax only returns the value after method call, it cannot be ensured that the value in this register will not be replaced (by tracking gcinterface :: collectgeneration finds that this function replaces the value in eax from the beginning ). Now that we understand the principle of determining reachable objects during garbage collection, we can make a small experiment and add this statement after forcing garbage collection:

Private void button#click (Object sender, eventargs E)
{
Tc to = new TC ();
GC. Collect ();
GC. waitforpendingfinalizers ();

String S = tc. tostring ();
Return;

}

At this time, the running result of the release is the same as that of the Debug. You must click the second time to execute the destructor. Let's look at the disassembly at this time:

MoV EBP, ESP
Push ESI
MoV ECx, 7d6714h (MT: test_winform.gc_test + Tc)
Call mscorwks! Jit_newfast (6cafc5d4)
MoV ESI, eax // assign the object address to ESI
MoV dword ptr [ESI + 4], 63 h
XOR edX, EDX
Lea ECx, [edx-1]
Call mscorwks! Gcinterface: collectgeneration (6cbbd082)
Call mscorwks! Gcinterface: runfinalizers (6cbe3491)
MoV ECx, ESI // returns ECx for the value stored in ESI
MoV eax, dword ptr [ECx]
Call dword ptr [eax + 28 h] // call tostring ()
Pop ESI
Pop EBP
RET 4
I tracked the entire GC function and found that although the ESI value is replaced in the function body, the ESI value is restored after the function is completed, and the eax value is not restored, I still don't understand the principle. In fact, nothing special in this article is just to prove the GC theory and JIT optimization capabilities.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.