I began to discuss this as part of my previous post, but I've preferred to set a whole post for a more complete discussion including some code.
So, from my previous post:
In .Net, an assembly is loaded the first time that a method referencing classes in that Assembly is Jitted. Jitting happens before running the method, so the runtime does not know if the instructions in that method that need that assembly will ever be really executed (they could be inside an if that never happens to be true...). When developing code with critical performance requirements, we should have this present, cause some minor modification to our code can save us an unnecessary Assembly load).
With the HotSpot JVM, due to the initial interpretation, class loading is not done on the method border, but on the instruction border. I mean, one class is not loaded until the first instruction that needs that class is interpreted.
OK, first let's go with the Java part:
public static void main(String[] args) throws java.io.IOException{ System.out.println("Started"); System.out.println("insert option: B or P"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String input = br.readLine(); System.out.println(input.length()); if (input.equals("P")){ Person p = new Person("Xana"); System.out.println("Person created"); } else{ Book b = new Book(); System.out.println("Book created"); } input = br.readLine(); }
When you run the code above with the -verbose option, you'll see that the Book or the Person class get loaded when the first use of such class is done (the new Person() or the new Book() statements). So, depending on whether the condition is true or not, only the Person or Book class will get loaded. This behaviour makes pretty much sense given that at first is the interpreter who is running, not the Jit.
You can grab the code here if you want to play with it yourself
For the .Net side, I had tested this some years ago, but as I found that sample a bit convoluted, I decided to write a new one:
class Parser { [MethodImpl(MethodImplOptions.NoInlining)] public void ParseFile(string fileType) { Console.WriteLine ("ParseFile"); if (fileType == "xml") { XmlDocument doc = new XmlDocument(); } else{/*do something here*/} } [MethodImpl(MethodImplOptions.NoInlining)] public void ParseFileAssemblyAware(string fileType) { Console.WriteLine ("ParseFileAssemblyAware"); if (fileType == "xml") this.ParseXml(); else{/*do something here*/} } [MethodImpl(MethodImplOptions.NoInlining)] private void ParseXml() { Console.WriteLine ("ParseXml"); XmlDocument doc = new XmlDocument(); } } class App { public static void Main(string[] args) { Console.WriteLine("started"); AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(delegate(object sender, AssemblyLoadEventArgs args1) { Console.WriteLine("ASSEMBLY LOADED: " + args1.LoadedAssembly.FullName); }); Parser parser = new Parser(); Console.WriteLine("file type (xml or other)?"); string fileType = Console.ReadLine(); Console.WriteLine("AssemblyLoadingAware (Y or N)?"); bool assemblyLoadingAware = Console.ReadLine().ToLower() == "y"; if(assemblyLoadingAware) parser.ParseFileAssemblyAware(fileType); else parser.ParseFile(fileType); } }The source
If you run the code above on Microsoft's CLR you'll find this:
When Main starts, the System.Xml assembly has not been loaded yet
If we say No to the "AssemblyLoadingAware" option, the System.Xml assembly gets loaded as we call into the ParseFile method (that's when the whole method is Jitted, and the Jitter sees that there's a reference to XmlDocument there, not taking into account that it's inside a conditional and could never be run) irrespective of whether we'll be parsing a xml file or other.
If we say Yes to the "AssemblyLoadingAware" option, if we have entered "other" instead of "xml", the System.Xml assembly will not be loaded. Notice that what we've done in that case is moving the Xml code to a separate method, that way, when the ParseFileAssemblyAware method is Jitted, no reference to XmlDocument is found there and the assembly is not loaded, being that loading put off to the moment when the ParseXml method is run for first time (that in this case never happens).
The bottom line here is that with .Net, if our software is performance critical (both in terms of memory, obviously a new Assembly in memory takes space, or in terms of time, as loading the Assembly also takes time...) we should pay some extra care to cases like the one shown above
Notice that I'm using the MethodImpl attribute for the methods in Person. I'm doing so to prevent the Jitter from inlining those methods (these are very short methods, so candidates to be inlined) when it Jits the Main method. If you run the sample without those attributes, you won't see any message about the System.Xml assembly getting loaded in any of the cases, that's just because it gets loaded just when it Jits the Main method, before we add the handler to the AssemblyLoad event (you can check with ProcessExplorer that System.Xml is already in memory in that case).
No comments:
Post a Comment