Search code examples
javaclassloaderdynamic-compilation

Possible to get native performance for dynamically compiled code that can be unloaded?


In .Net, it seems you cannot

  1. dynamically compile code
  2. call the compiled code directly (i.e. w/o "remoting", marshaling, etc) and
  3. remove (only) the compiled code from memory

You have to decide between 2. (by generating the code into the calling AppDomain itself) or 3. (by generating the code into a throw-away AppDomain), but you cannot have both.

Now I'm curious if this is possible in Java. I don't know enough about ClassLoaders, but it seems in Java I could

  1. dynamically compile code into a throw-away class loader
  2. call the compiled code (say, through a virtual method call to predefined interface) directly, w/o any marshaling
  3. remove all references to compiled class and throw-away class loader so GC will take care of removal

Is that assumption valid?


Solution

  • Yes, you can compile / load a class a code away class loader, the call it without issue.

    Yes, dynamically code will reach 'full performance'. There no difference. However, the newly loaded code will start in interpreted mode and needs to warm up before it is compiled.

    However, point 3. is very tricky.

    • 'Leaking' the throw away class loader is very easy / likely. The class loader keeps references to it's loaded classes. Each class holds a reference to it's classloader. Each object a reference to it's class. Therefore, as long as you have a reference to a object or class which as loaded with the throw away class loader, it and it's loaded classes stay alive. Because is so easy to have a reference to a object, 'classloader' leaks are very common.
    • It depends on GC configuration and JVM version is it actually does GC passes over on loaded classes. You might need extra flags to enable it. Like for CMS GC '-XX:+CMSClassUnloadingEnabled'.
    • There is a Code cache (in OpenJDK/Hotspot), which keeps the compiled code. If you keep loading code through all you're app's lifetime, you might overrun this cache. In older JVM's it just filled up, and once full, it just stopped compiling code, degrading in performance, unless you enabled flushing that cache (-XX:+UseCodeCacheFlushing). Afaik in newer releases it flushes by default. Double check it. You might want to keep an eye on the code cache. (via JMX for example)