Posts Tagged ‘DynamicLanguages’
[DevoxxFR2013] Invokedynamic in 45 Minutes: Unlocking Dynamic Language Performance on the JVM
Lecturer
Charles Nutter has spent over a decade as a Java developer and more than six years leading the JRuby project at Red Hat. Co-lead of JRuby, he works to fuse Ruby’s elegance with the JVM’s power while contributing to other JVM languages and educating the community on advanced virtual machine capabilities. A proponent of open standards, he aims to keep the JVM the premier managed runtime through innovations like invokedynamic.
Abstract
Charles Nutter demystifies invokedynamic, the JVM bytecode instruction introduced in Java 7 to optimize dynamic language implementation. He explains its mechanics—bootstrap methods, call sites, and method handles—through progressive examples, culminating in a toy language interpreter. The presentation contrasts invokedynamic with traditional invokevirtual and invokeinterface, benchmarks performance, and illustrates how it enables JRuby and other languages to approach native Java speeds, paving the way for polyglot JVM ecosystems.
The Problem with Traditional Invocation: Static Assumptions in a Dynamic World
Nutter begins with the JVM’s historical bias toward statically-typed languages. The four classic invocation instructions—invokevirtual, invokeinterface, invokestatic, and invokespecial—assume method resolution at class loading or compile time. For dynamic languages like Ruby, Python, or JavaScript, method existence and signatures are determined at runtime, forcing expensive runtime checks or megamorphic call sites.
JRuby, prior to invokedynamic, relied on reflection or generated bytecodes per call, incurring significant overhead. Even interface-based dispatch suffered from inline cache pollution when multiple implementations competed.
Invokedynamic Mechanics: Bootstrap, Call Sites, and Method Handles
Introduced via JSR-292, invokedynamic defers method linking to a user-defined bootstrap method (BSM). The JVM invokes the BSM once per call site, passing a CallSite object, method name, and type. The BSM returns a MethodHandle—a typed, direct reference to a method—installed into the call site.
Nutter demonstrates a simple BSM:
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) {
MethodHandle mh = lookup.findStatic(MyClass.class, "target", type);
return new ConstantCallSite(mh);
}
The resulting invokedynamic instruction executes the linked handle directly, bypassing vtable lookups.
Call Site Types and Guarded Invocations
Call sites come in three flavors: ConstantCallSite for immutable linkages, MutableCallSite for dynamic retargeting, and VolatileCallSite for atomic updates. Guarded invocations combine a test (guard) with a target handle:
MethodHandle guard = lookup.findStatic(Guards.class, "isString", MethodType.methodType(boolean.class, Object.class));
MethodHandle target = lookup.findStatic(Handlers.class, "handleString", type);
MethodHandle fallback = lookup.findStatic(Handlers.class, "handleOther", type);
MethodHandle guarded = MethodHandles.guardWithTest(guard, target, fallback);
The JVM inlines the guard, falling back only on failure, enabling polymorphic inline caches.
Building a Toy Language: From Parser to Execution
Nutter constructs a minimal scripting language with arithmetic and print statements. The parser generates invokedynamic instructions with a shared BSM. The BSM resolves operators (+, -, *) to overloaded Java methods based on argument types, caching results per call site.
Execution flows through method handles, achieving near-Java performance. He extends the example to support runtime method missing, emulating Ruby’s method_missing.
Performance Analysis: Benchmarking Invocation Strategies
Nutter presents JMH benchmarks comparing invocation types. invokestatic serves as baseline; invokevirtual adds vtable dispatch; invokeinterface incurs interface check. invokedynamic with ConstantCallSite matches invokestatic, while MutableCallSite aligns with invokevirtual.
Key insight: the JVM’s optimizer treats stable invokedynamic sites as monomorphic, inlining aggressively. JRuby leverages this for core methods, reducing dispatch overhead by 10-100x.
Implications for JVM Languages and Future Evolution
Invokedynamic enables true polyglot JVMs. Nashorn (JavaScript), Dynalink, and Truffle frameworks build upon it. Future enhancements include value types and specialized generics, further reducing boxing.
Nutter concludes that invokedynamic fulfills John Rose’s vision: dynamic dispatch no slower than static, ensuring the JVM’s longevity as a universal runtime.