How to prove that the .NET CLR JIT compiles every method only once per run?

旧时模样 提交于 2019-12-10 21:06:30

问题


There's an old question asking whether C# is JIT compiled every time and the answer by famous Jon Skeet is: "no, it's compiled only once per application" as long as we're talking about desktop applications which are not NGENed.

I want to know if that information from 2009 is still true and I want to figure that out by experiment and debugging, potentially by putting a breakpoint on the JITter and using WinDbg commands to inspect objects and methods.

My research so far

I know that the .NET memory layout considers a Header (at address A-4) and a Method Table (at address A+0) per object before the actual data starts (at address A+4). So it would be possible that each object has a different method table and thus could have different JITted methods.

Why do I have doubts about the correctness of the statement?

We had a workshop for parallel programming and one claim by the trainer was that methods are JITted for every object per thread. That clearly didn't make sense to me and I was able to write a counter example application.

Unfortunately, the following other topics came up, for which I also want to write a demonstration:

  • new .NET frameworks
  • application domains
  • code access security

The linked answer was written when .NET 3.5 was released. It was not substantially changed since then, i.e. it has not received updates for .NET 4.0, 4.6 and 4.6.

Regarding application domains, my personal opinion is that I could unload an application domain, which unloads assemblies. If an assembly is unloaded, it's gone and the IL code goes with it. I don't see much benefit in keeping native code for IL code which was destroyed. Therefore, I could imagine that creating an application domain and loading the assembly again might result in JITting the method again.

Regarding code access security, I'm not sure if it is considered by the JIT compiler based on the current permissions or whether it's done by reflection at runtime. If it's done by the JIT compiler, the compiled code will differ, depending on the permission set.


回答1:


The "design principle" of the JIT is to compile a method (an instantiation of method for generic methods) once and and reuse the same native code. Of course, the implementation is extremely complicated and I'll try simplify the answer without compromising accuracy. The answer is the same for all versions of the runtime since .NET 2.0 and up to the latest .NET 4.6 (I don't know about .NET 1.x, probably the same).

The runtime profiler callbacks and the ETW events are poorly documented. Both of them occur when JIT compilation is attempted, but not necessarily succeeded. There are three cases where this can happen: 1- the method fails to meet certain security requirements, 2- the verification of the method failed, and 3- memory could not be allocated to hold the native code to be emitted. Therefore, the JIT start callback and event can overestimate the number of times a method has actually been compiled. Similarly, the JIT completion callback and event are inaccurate. There are few rarely occurring cases in which they might underestimate the number of times the same method has been successfully compiled. It's worth mentioning at this point that the # of Methods JITted performance counter reports accurately the number of times all IL methods have been compiled collectively in all appdomains of a process.

For appdomain-specific assemblies, methods are compiled in each appdomain separately. There is no sharing (even though it can be technically possible sometimes). For appdomain-neutral assemblies, the runtime attempts to compile each method once and share the native code with all appdomains.

How to prove that the .NET CLR JIT compiles every method only once per run?

Well, in some cases (such as when using background JIT, and other extremely subtle cases), a method can get compiled without ever getting executed. So saying every method is compiled once per run is inaccurate.

You can refer to the CoreCLR JIT source code for more information (the JIT is the same one used in the .NET framework 4.5+ but this answer applies to older versions since the JIT triggering mechanism is mostly the same). The source code is the proof.

methods are JITted for every object per thread

Yes, that doesn't make any sense. The scope of compilation is appdomains.



来源:https://stackoverflow.com/questions/42262201/how-to-prove-that-the-net-clr-jit-compiles-every-method-only-once-per-run

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!