{"id":515,"date":"2017-05-09T11:19:14","date_gmt":"2017-05-09T03:19:14","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/seteplia\/?p=515"},"modified":"2019-06-11T22:42:03","modified_gmt":"2019-06-12T05:42:03","slug":"garbage-collection-and-variable-lifetime-tracking","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/garbage-collection-and-variable-lifetime-tracking\/","title":{"rendered":"Garbage collection and variable lifetime tracking"},"content":{"rendered":"<p>Here is a seemingly simple question for you: <b>Is it possible that the CLR will call a finalizer for an instance when an instance method is still running? <\/b>In other words, is it possible in the following case to see \u2018Finalizing instance.\u2019 before \u2018Finished doing something.\u2019?<\/p>\n<pre class=\"lang:default decode:true \">internal class GcIsWeird\r\n{\r\n    ~GcIsWeird()\r\n    {\r\n        Console.WriteLine(\"Finalizing instance.\");\r\n    }\r\n\r\n    public int data = 42;\r\n\r\n    public void DoSomething()\r\n    {\r\n        Console.WriteLine(\"Doing something. The answer is ... \" + data);\r\n        \/\/ Some other code...\r\n        Console.WriteLine(\"Finished doing something.\");\r\n    }\r\n}<\/pre>\n<p>The answer is: \u201cIt depends\u201d. In debug builds this will never happen (as far as I can tell), but in release builds this is possible. To simplify this discussion, let\u2019s consider the following static method:<\/p>\n<pre class=\"lang:default decode:true \">static void SomeWeirdAndVeryLongRunningStaticMethod()\r\n{\r\n    var heavyWeightInstance = new int[42_000_000];\r\n    \/\/ The very last reference to 'heavyWeightInstance'\r\n    Console.WriteLine(heavyWeightInstance.Length);\r\n \r\n    for (int i = 0; i &lt; 10_000; i++)\r\n    {\r\n        \/\/ Doing some useful stuff.\r\n        Thread.Sleep(42);\r\n    }\r\n}<\/pre>\n<p>The local variable \u2018heavyWeightInstance\u2019 is used only in first two lines and theoretically can be collected by the GC after that. One might set the variable to <b>null <\/b>explicitly to release the reference, but this is not needed. The CLR has an optimization that allows collection of instances once they are no longer being used. The JIT compiler emits a special table, called \u2018Pointer Table\u2019 or (GCInfo, see <a href=\"https:\/\/github.com\/dotnet\/coreclr\/blob\/master\/src\/jit\/gcinfo.cpp\">gcinfo.cpp<\/a> at <a href=\"https:\/\/github.com\/dotnet\/coreclr\/\">coreclr repo<\/a>) that gives the GC enough information to decide when a variable is reachable and when is not.<\/p>\n<p>An instance method is just a static method with an \u2018instance\u2019 pointer passed via the first argument. This means that all reachability optimizations are valid for both instance and static methods.<\/p>\n<p>To prove that this is indeed the case, we can run the following program and look at the output.<\/p>\n<pre class=\"lang:default decode:true \">using System;\r\n \r\ninternal class GcIsWeird\r\n{\r\n    ~GcIsWeird()\r\n    {\r\n        Console.WriteLine(\"Finalizing instance.\");\r\n    }\r\n \r\n    public int data = 42;\r\n \r\n    public void DoSomething()\r\n    {\r\n        Console.WriteLine(\"Doing something. The answer is ... \" + data);\r\n        CheckReachability(this);\r\n        Console.WriteLine(\"Finished doing something.\");\r\n    }\r\n \r\n    static void CheckReachability(object d)\r\n    {\r\n        var weakRef = new WeakReference(d);\r\n        Console.WriteLine(\"Calling GC.Collect...\");\r\n \r\n        GC.Collect();\r\n        GC.WaitForPendingFinalizers();\r\n        GC.Collect();\r\n \r\n        string message = weakRef.IsAlive ? \"alive\" : \"dead\";\r\n        Console.WriteLine(\"Object is \" + message);\r\n    }\r\n}\r\n \r\nclass Program\r\n{\r\n    static void Main(string[] args)\r\n    {\r\n        new GcIsWeird().DoSomething();\r\n    }\r\n}<\/pre>\n<p>As we would expect, running this program in release mode and with no debugger attached will lead to the following output:<\/p>\n<p><span style=\"font-family: Consolas;\">Doing something. The answer is &#8230; 42\nCalling GC.Collect&#8230;\n<strong>Finalizing instance.<\/strong>\nObject is dead\n<strong>Finished doing something.<\/strong>\n<\/span><\/p>\n<p>The output shows that the object was collected during the execution of the instance method. Now let\u2019s look at how this happens.<\/p>\n<p>There are a few options for doing that. First, you can use WinDbg and run <b>!GCInfo<\/b> command for a given method table (here is a good blog post for you, <a href=\"https:\/\/blogs.msdn.microsoft.com\/yunjin\/2005\/05\/15\/figure-out-variable-lifetime-using-sos\/\">Figure out variable lifetime using SOS<\/a>). Second, you can build CoreClr and run your application with JIT trace enabled.<\/p>\n<p>The first option seemed simpler, however I\u2019ve decided in this case to use the second one. And, believe me, the second option is not as complicated as you would think. You just need to follow the instructions \u2013 <a href=\"https:\/\/github.com\/dotnet\/coreclr\/blob\/master\/Documentation\/building\/viewing-jit-dumps.md\">Viewing JIT Dumps<\/a> and do the following:<\/p>\n<ol>\n<li>Build CoreCLR Repo (don\u2019t forget to install all the required stuff, like VC++, CMake and Python).<\/li>\n<li>Install dotnet cli.<\/li>\n<li>Create a dotnet core app.<\/li>\n<li>Build and publish dotnet core app.<\/li>\n<li>Copy freshly built coreclr binaries into the published folder with your app.<\/li>\n<li>Set some flags, like COMPlus_JitDump=YourMethodName.<\/li>\n<li>Run the app.<\/li>\n<\/ol>\n<p>And here is the output:<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Consolas;\">*************** After end code gen, before unwindEmit()\nIN0002: 000012 call\u00a0\u00a0\u00a0\u00a0 CORINFO_HELP_NEWSFAST\nIN0003: 000017 mov\u00a0\u00a0\u00a0\u00a0\u00a0 rcx, 0x1FE90003070<\/span><\/p>\n<p>\/\/ Console.WriteLine(&#8220;Doing something. The answer is &#8230; &#8221; + data);\nIN0004: 000021 mov\u00a0\u00a0\u00a0\u00a0\u00a0 rcx, gword ptr [rcx]\nIN0005: 000024 mov\u00a0\u00a0\u00a0\u00a0\u00a0 edx, dword ptr [rsi+8]\nIN0006: 000027 mov\u00a0\u00a0\u00a0\u00a0\u00a0 dword ptr [rax+8], edx\nIN0007: 00002A mov\u00a0\u00a0\u00a0\u00a0\u00a0 rdx, rax<\/p>\n<p><span style=\"font-family: Consolas;\">IN0008: 00002D call\u00a0\u00a0\u00a0\u00a0 System.String:Concat(ref,ref):ref\nIN0009: 000032 mov\u00a0\u00a0\u00a0\u00a0\u00a0 rcx, rax\nIN000a: 000035 call\u00a0\u00a0\u00a0\u00a0 System.Console:WriteLine(ref)<\/span><\/p>\n<p>\/\/ CheckReachability(this);\n<strong>IN000b: 00003A mov\u00a0\u00a0\u00a0\u00a0\u00a0 rcx, rsi<\/strong>\n\/\/ After this point, &#8216;this&#8217; pointer is reachable for GC\nIN000c: 00003D call\u00a0\u00a0\u00a0\u00a0 Reachability.Program:CheckReachability(ref)\n\/\/ Console.WriteLine\nIN000d: 000042 mov\u00a0\u00a0\u00a0\u00a0\u00a0 rcx, 0x1FE90003078\nIN000e: 00004C mov\u00a0\u00a0\u00a0\u00a0\u00a0 rcx, gword ptr [rcx]\nIN000f: 00004F mov\u00a0\u00a0\u00a0\u00a0\u00a0 rax, 0x7FFB6C6B0160<\/p>\n<p><span style=\"font-family: Consolas;\">*************** Variable debug info\n2 vars\n0(\u00a0\u00a0 UNKNOWN) : From 00000000h to 00000008h, in rcx\n<strong>\u00a0 0(\u00a0\u00a0 UNKNOWN) : From 00000008h to 0000003Ah, in rsi<\/strong>\n*************** In gcInfoBlockHdrSave()\nRegister slot id for reg rsi = 0.\nSet state of slot 0 at instr offset 0x12 to Live.\nSet state of slot 0 at instr offset 0x17 to Dead.\nSet state of slot 0 at instr offset 0x2d to Live.\nSet state of slot 0 at instr offset 0x32 to Dead.\nSet state of slot 0 at instr offset 0x35 to Live.\n<strong>Set state of slot 0 at instr offset 0x3a to Dead.<\/strong><\/span><\/p>\n<p>&nbsp;<\/p>\n<p>The dump from the JIT compiler is slightly different from the one you might get from WinDBG or theVisual Studio \u2018Disassembly\u2019 window. The main difference is that it shows way more information, including the number of local variables (when they were used in terms of ASM offsets) and the GCInfo. Another useful aspect that it shows instruction offset that helps to understand the GCInfo table.<\/p>\n<p>In this case, it is clear that \u2018this\u2019 pointer is no longer needed after instruction <b>0x3A<\/b>, i.e. right before the call to <b>CheckReachability<\/b>. And this is the very reason, why the instance was collected once GC was called inside <b>CheckReachability<\/b> method.<\/p>\n<h4>Conclusion<\/h4>\n<p>The main idea of this blog post is to show that there is no magic happening during GC. The JIT and the GC are working together to track some auxiliary information that helps the GC to clean objects as soon as possible.<\/p>\n<p>But I want you to be cautious with this knowledge. The C# language specification states that this optimization is possible but not required: &#8220;if a local variable that is in scope is the only existing reference to an object, but that local variable is never referred to in any possible continuation of execution from the current execution point in the procedure, the garbage collector may (but is not required to) treat the object as no longer in use&#8221;. So you should not rely on this behavior in your production scenarios.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here is a seemingly simple question for you: Is it possible that the CLR will call a finalizer for an instance when an instance method is still running? In other words, is it possible in the following case to see \u2018Finalizing instance.\u2019 before \u2018Finished doing something.\u2019? internal class GcIsWeird { ~GcIsWeird() { Console.WriteLine(&#8220;Finalizing instance.&#8221;); } [&hellip;]<\/p>\n","protected":false},"author":4004,"featured_media":37840,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[6700,6698],"tags":[6695],"class_list":["post-515","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-internals","category-gc","tag-seteplia"],"acf":[],"blog_post_summary":"<p>Here is a seemingly simple question for you: Is it possible that the CLR will call a finalizer for an instance when an instance method is still running? In other words, is it possible in the following case to see \u2018Finalizing instance.\u2019 before \u2018Finished doing something.\u2019? internal class GcIsWeird { ~GcIsWeird() { Console.WriteLine(&#8220;Finalizing instance.&#8221;); } [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/4004"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=515"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/515\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/37840"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}