<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Valhalla on Johan Sjölén&#39;s blog</title>
        <link>https://johan-sjolen.github.io/tags/valhalla/</link>
        <description>Recent content in Valhalla on Johan Sjölén&#39;s blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Fri, 21 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://johan-sjolen.github.io/tags/valhalla/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>Value Classes Still Need Compiler Sympathy</title>
            <link>https://johan-sjolen.github.io/post/compiler-sympathy/compiler-sympathy/</link>
            <pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate>
            <guid>https://johan-sjolen.github.io/post/compiler-sympathy/compiler-sympathy/</guid>
            <description>&lt;p&gt;&lt;em&gt;This post discusses preview features in JDK 28&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://openjdk.org/jeps/401&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;JEP 401&lt;/a&gt;, a major Valhalla milestone, has been integrated as a preview feature in JDK 28. This is very exciting, as value classes increase both our ability to communicate the semantics of our programs to others and the optimization opportunities available to the JVM.&lt;/p&gt;&#xA;&lt;p&gt;However, I have seen people online essentially taking a &amp;ldquo;Value all the classes!&amp;rdquo; approach to this. I worry that there is a belief that ordinary classes give you a floor on performance, and that value classes will do their best to raise you above that floor, but won&amp;rsquo;t ever take you below it. Unfortunately, that is not true. A well-intentioned program may put the JVM into a situation where a flattened representation is faster for some methods, and a reference representation is faster for others. When these methods interact, the JVM is forced to convert between the two representations. I want value classes to be more than just magic, so today I am going to show you what the JVM is capable of &lt;strong&gt;right now&lt;/strong&gt;, and where its limitations are. I hope that with this you&amp;rsquo;ll have some context for reasoning about the code that you (or your AI agent) write.&lt;/p&gt;&#xA;&lt;p&gt;The main optimization advantage of value classes is that we give up identity. This gives the JVM freedom to choose a suitable representation for a particular situation. Without the requirement of identity, the runtime can more readily flatten values (avoiding pointer chasing), scalarize them by representing their components independently in registers or on the stack. For the value object itself, escape analysis becomes trivial: there is no identity whose escape must be proven unobservable.&lt;/p&gt;&#xA;&lt;p&gt;We are going to examine three examples: a large final value stored flat, a direct value transformation compiled without allocation, and a generic virtual call that requires materialization.&lt;/p&gt;&#xA;&lt;h2 id=&#34;immutability-enables-flattening&#34;&gt;Immutability enables flattening&#xA;&lt;/h2&gt;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://openjdk.org/jeps/539&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;JEP 539&lt;/a&gt;, Strict Field Initialization in the JVM, lets the JVM rely on a final field having been initialized before its enclosing object becomes observable. Because such a field cannot later be updated, the JVM may use a non-atomic flattened layout without risking a torn assignment. Mutable fields, however, must preserve tear-free assignment. If a mutable field contains a value that is too large for an atomic flattened update, the JVM must instead use a reference layout. The strict-initialization guarantee opens up many optimization possibilities.&lt;/p&gt;&#xA;&lt;p&gt;Consider this small example:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;value record &lt;span style=&#34;color:#a6e22e&#34;&gt;FourLongs&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; b, &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; c, &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; d) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;record&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Envelope&lt;/span&gt;(FourLongs payload) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;FourLongs&lt;/code&gt; has 32 bytes of payload, making it too large for an atomic flattened update in the current JVM. But &lt;code&gt;Envelope.payload&lt;/code&gt; is a record component and therefore a strictly initialized final field: once initialized, it is never updated. The JVM is consequently free to store &lt;code&gt;payload&lt;/code&gt; using a non-atomic flattened layout. In the current Valhalla master build, the field-layout diagnostic reports the following when using &lt;code&gt;PrintFieldLayout&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Layout of class FourLongs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  @8  REGULAR 8/8 &amp;#34;a&amp;#34; J&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  @16 REGULAR 8/8 &amp;#34;b&amp;#34; J&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  @24 REGULAR 8/8 &amp;#34;c&amp;#34; J&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  @32 REGULAR 8/8 &amp;#34;d&amp;#34; J&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  @40 NULL_MARKER 1/1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  NULLABLE_NON_ATOMIC_FLAT layout: 33/8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Layout of class Envelope&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  @8 FLAT 33/8 &amp;#34;payload&amp;#34; LFourLongs;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      FourLongs NULLABLE_NON_ATOMIC_FLAT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we can see that a &lt;code&gt;FourLongs&lt;/code&gt; consists of its four components and a 1-byte null marker, and that it supports a nullable, non-atomic flattened layout. The runtime uses this fact in the &lt;code&gt;Envelope&lt;/code&gt; record, and allows &lt;code&gt;FourLongs&lt;/code&gt; to be flattened. The key point is that &lt;code&gt;Envelope&lt;/code&gt; is also immutable; the layout would have to change if we replaced it with a mutable class:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MutableEnvelope&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; FourLongs payload;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MutableEnvelope&lt;/span&gt;(FourLongs payload) { &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;payload&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; payload; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Layout of class MutableEnvelope&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  @8 REGULAR 4/4 &amp;#34;payload&amp;#34; LFourLongs;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Why is that? Let&amp;rsquo;s consider a data race between two threads:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;thread1&lt;/span&gt;(MutableEnvelope a) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    a.&lt;span style=&#34;color:#a6e22e&#34;&gt;payload&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FourLongs(1, 0, 0, 0);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;thread2&lt;/span&gt;(MutableEnvelope a) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    a.&lt;span style=&#34;color:#a6e22e&#34;&gt;payload&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FourLongs(0, 1, 0, 0);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;throws&lt;/span&gt; InterruptedException {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    MutableEnvelope a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; MutableEnvelope(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FourLongs(0, 0, 0, 0));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; t1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Thread(() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; thread1(a));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; t2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Thread(() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; thread2(a));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    t1.&lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    t2.&lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    t1.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    t2.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IO.&lt;span style=&#34;color:#a6e22e&#34;&gt;println&lt;/span&gt;(a.&lt;span style=&#34;color:#a6e22e&#34;&gt;payload&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Writing a flattened field requires writing its individual components. If thread1 and thread2 wrote those components independently, another thread could observe a torn value such as &lt;code&gt;(1, 1, 0, 0)&lt;/code&gt;, assembled from parts of two different assignments. The Java Memory Model forbids such tearing: after both threads have joined, this program may print only &lt;code&gt;(1, 0, 0, 0)&lt;/code&gt; or &lt;code&gt;(0, 1, 0, 0)&lt;/code&gt;. Guaranteeing tear-free assignment for a flattened value this large would be expensive, so the current JVM uses a reference layout. Each thread constructs a complete FourLongs and then performs an atomic reference store.&lt;/p&gt;&#xA;&lt;h2 id=&#34;removing-identity-removes-the-allocation&#34;&gt;Removing identity removes the allocation&#xA;&lt;/h2&gt;&lt;p&gt;If we have a small function that changes a component in a loop, like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; FourLongs &lt;span style=&#34;color:#a6e22e&#34;&gt;bumpA&lt;/span&gt;(FourLongs value) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FourLongs(value.&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1, value.&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;(), value.&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;(), value.&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;());&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; iterations) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    FourLongs value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FourLongs(0, 2, 3, 4);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; iterations; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; bumpA(value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; value.&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; value.&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; value.&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; value.&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At the source level, we can see that every call to &lt;code&gt;bumpA&lt;/code&gt; constructs a new &lt;code&gt;FourLongs&lt;/code&gt;. At the call site of &lt;code&gt;run&lt;/code&gt;, however, we can see that the returned value is effectively only there to change the &lt;code&gt;a&lt;/code&gt; component of &lt;code&gt;value&lt;/code&gt;. A good optimizing compiler ought to be able to recognize that as well. It turns out that C2 is a pretty good compiler! C2 keeps the representation scalarized, and it even recognizes that the final result must be &lt;code&gt;iterations + (2 + 3 + 4) = iterations + 9&lt;/code&gt; when &lt;code&gt;iterations &amp;gt;= 0&lt;/code&gt;. The following is an abridged excerpt of the generated code:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mov&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#9               ; Put 9 into x0, which holds the return value&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cmp&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x1&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#0               ; Compare x1 (contains iterations) with 0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b.le&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;                 &lt;span style=&#34;color:#75715e&#34;&gt;; If less or equal to 0, jump to done&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;w1&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;sxtw&lt;/span&gt;     &lt;span style=&#34;color:#75715e&#34;&gt;; Set x0 = x0 + w1, sign-extending w1 to 64 bits&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;done:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ret&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we keep &lt;code&gt;bumpA&lt;/code&gt; unchanged but make &lt;code&gt;FourLongs&lt;/code&gt; an identity record, C2 must prove that its identity has no impact on the computation. Compilers can often do this, but now we depend on the compiler proving it in each case. When I tried to do this (by removing &lt;code&gt;value&lt;/code&gt; from the &lt;code&gt;FourLongs&lt;/code&gt; declaration), C2 was not capable of performing this optimization.&lt;/p&gt;&#xA;&lt;p&gt;The following is an abridged excerpt of C2&amp;rsquo;s compilation of &lt;code&gt;IdentityRecordExperiment::run&lt;/code&gt;. The allocation remains in &lt;code&gt;run&lt;/code&gt;&amp;rsquo;s loop even though &lt;code&gt;bumpA&lt;/code&gt; has been inlined:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# {method} static &amp;#39;run&amp;#39; &amp;#39;(J)J&amp;#39; in &amp;#39;IdentityRecordExperiment&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; initial FourLongs allocation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ldr&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;,  [&lt;span style=&#34;color:#66d9ef&#34;&gt;x28&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#TLAB_TOP]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ldr&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x10&lt;/span&gt;, [&lt;span style=&#34;color:#66d9ef&#34;&gt;x28&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#TLAB_END]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x11&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#0x28          ; reserve 40 bytes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cmp&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x11&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;x10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b.hs&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;slow_allocation&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; object-header setup omitted&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;str&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x11&lt;/span&gt;, [&lt;span style=&#34;color:#66d9ef&#34;&gt;x28&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#TLAB_TOP]   ; commit allocation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; object initialization omitted&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; loop body: allocation from inlined bumpA&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ldr&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;,  [&lt;span style=&#34;color:#66d9ef&#34;&gt;x28&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#TLAB_TOP]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ldr&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x10&lt;/span&gt;, [&lt;span style=&#34;color:#66d9ef&#34;&gt;x28&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#TLAB_END]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x11&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#0x28          ; reserve another 40 bytes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;cmp&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x11&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;x10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b.hs&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;slow_allocation&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; object-header setup omitted&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;str&lt;/span&gt;     &lt;span style=&#34;color:#66d9ef&#34;&gt;x11&lt;/span&gt;, [&lt;span style=&#34;color:#66d9ef&#34;&gt;x28&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#TLAB_TOP]   ; commit allocation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; object initialization omitted&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clearly, providing the compiler with stronger semantic guarantees can sometimes produce a very big win.&lt;/p&gt;&#xA;&lt;h2 id=&#34;type-erasure-brings-the-allocation-back&#34;&gt;Type erasure brings the allocation back&#xA;&lt;/h2&gt;&lt;p&gt;Now we are going to look at something a bit more complex. This example is derived from an &lt;a class=&#34;link&#34; href=&#34;https://mail.openjdk.org/archives/list/valhalla-dev@openjdk.org/thread/3P5463B2OWURWM4LIMYIQMQOMZCOR6LN/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;email&lt;/a&gt; we received from a user on the valhalla-dev mailing list. He had ported a parsing library from Elm to Java and noticed a slowdown after converting all of his records to value records.&#xA;A performance regression is obviously not the result we want, but an unexpected result like this is fascinating: value classes give the JVM more semantic information and greater freedom, so how could using them make the program slower? I did a deep dive to find the cause and a source-level fix. This investigation has opened up an interesting compiler problem that my colleagues on the C2 team are now actively investigating. I&amp;rsquo;ll explain my findings here, but please keep in mind that I&amp;rsquo;ve had to simplify this greatly. The JVM and &lt;code&gt;javac&lt;/code&gt; are both fairly complex, so I have to leave out details.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;value record &lt;span style=&#34;color:#a6e22e&#34;&gt;LargeValue&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; b, &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; c, &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; d) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;value record &lt;span style=&#34;color:#a6e22e&#34;&gt;Carrier&lt;/span&gt;(LargeValue v, &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; b) {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Fun&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;R, F&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    R &lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(F value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Frobber&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;extends&lt;/span&gt; Fun&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Carrier, LargeValue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FrobIt&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;implements&lt;/span&gt; Frobber {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; Carrier &lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(LargeValue value) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Carrier(value, &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GrobIt&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;implements&lt;/span&gt; Frobber { &lt;span style=&#34;color:#75715e&#34;&gt;/* impl omitted on purpose */&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DrobIt&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;implements&lt;/span&gt; Frobber { &lt;span style=&#34;color:#75715e&#34;&gt;/* impl omitted on purpose */&lt;/span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is pretty simple code. We have multiple &lt;code&gt;Frobber&lt;/code&gt;s that take a &lt;code&gt;LargeValue&lt;/code&gt; and produce a &lt;code&gt;Carrier&lt;/code&gt;, which contains another &lt;code&gt;LargeValue&lt;/code&gt;. The &lt;code&gt;Frobber&lt;/code&gt; interface extends the &lt;code&gt;Fun&amp;lt;R, F&amp;gt;&lt;/code&gt; interface.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s look at this through the lens of the JVM so that we can understand what is happening. Java implements generics through type erasure, replacing these type parameters with &lt;code&gt;Object&lt;/code&gt;. That means that &lt;code&gt;Frobber&lt;/code&gt; effectively inherits this method as far as the JVM is concerned:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Frobber&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;extends&lt;/span&gt; Fun {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Object &lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(Object value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The typed signature &lt;code&gt;Carrier apply(LargeValue)&lt;/code&gt; does not appear in the inherited JVM method descriptor and therefore has to be recovered through dynamic analysis. To accommodate this type discrepancy, &lt;code&gt;javac&lt;/code&gt; generates bridge methods in the class file. Each implementation gains a method approximately equivalent to this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Generated by javac&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; Object &lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(Object value) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; apply((LargeValue) value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The bridge accepts the erased argument, casts it to the expected type, and invokes the method we actually wrote. Now consider the method from the original reproducer:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; Carrier &lt;span style=&#34;color:#a6e22e&#34;&gt;reproduce&lt;/span&gt;(LargeValue value, Frobber a, Frobber b) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Carrier c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; a.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; b.&lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(c.&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;());&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are two separate interface call sites here. If each call site only observes one implementation, C2 can devirtualize it. For example, the first call site might always receive a &lt;code&gt;FrobIt&lt;/code&gt;, while the second always receives a &lt;code&gt;GrobIt&lt;/code&gt;. C2 can independently guard and inline both targets. In this experiment, C2 managed to remove all heap allocations. In pseudo-Java, the result looks like this. We represent scalarized values (values that are not heap references) by appending &lt;code&gt;Fields&lt;/code&gt; to the type name:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; CarrierFields &lt;span style=&#34;color:#a6e22e&#34;&gt;reproduce&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        LargeValueFields value,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Frobber a,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Frobber b) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    guard(classOf(a) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; FrobIt.&lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CarrierFields c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; inline(FrobIt_apply(value));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    guard(classOf(b) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; GrobIt.&lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; inline(GrobIt_apply(c.&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this level, there is no longer a call to the generated bridge. Devirtualizing and inlining the target also inlines its bridge. Once the bridge has disappeared into the surrounding compilation, there is no longer a real &lt;code&gt;Object apply(Object)&lt;/code&gt; call boundary.&lt;/p&gt;&#xA;&lt;p&gt;In the report we received, however, the call sites were megamorphic. In our example, that means &lt;code&gt;FrobIt&lt;/code&gt;, &lt;code&gt;GrobIt&lt;/code&gt;, and &lt;code&gt;DrobIt&lt;/code&gt; were all called interchangeably. With three hot implementations at each call site, C2 leaves the calls dynamically dispatched:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;invokeinterface Frobber.apply:(Object)Object&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The inherited method descriptor defines an ABI that requires callers to pass object references and implementations to return object references.&lt;/p&gt;&#xA;&lt;p&gt;The caller currently has a scalarized &lt;code&gt;LargeValue&lt;/code&gt;, but &lt;code&gt;Object apply(Object)&lt;/code&gt; cannot accept four independent scalar components. It requires a genuine reference. The caller must therefore materialize the value before making the call.&lt;/p&gt;&#xA;&lt;p&gt;The dynamically selected bridge then has to translate in the other direction:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Object &lt;span style=&#34;color:#a6e22e&#34;&gt;FrobIt_apply_bridge&lt;/span&gt;(Object argument) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LargeValueFields value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            scalarize((LargeValue) argument);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CarrierFields result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            FrobIt_apply_typed(value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; materializeCarrier(result);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The bridge casts the incoming reference to &lt;code&gt;LargeValue&lt;/code&gt;, extracts its components, and invokes the typed implementation using the scalarized value-object calling convention. The typed implementation returns a scalarized &lt;code&gt;Carrier&lt;/code&gt;, but the bridge itself promises to return &lt;code&gt;Object&lt;/code&gt;, so it must materialize the result before returning.&lt;/p&gt;&#xA;&lt;p&gt;The megamorphic version of &lt;code&gt;reproduce&lt;/code&gt; therefore looks approximately like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; CarrierFields &lt;span style=&#34;color:#a6e22e&#34;&gt;reproduce&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        LargeValueFields value,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Frobber a,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Frobber b) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LargeValue argument1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; materializeLargeValue(value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Object returned1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            invokeinterface_apply_Object(a, argument1);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Carrier carrier1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (Carrier) returned1;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CarrierFields c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scalarize(carrier1);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LargeValue argument2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            materializeLargeValue(c.&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Object returned2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            invokeinterface_apply_Object(b, argument2);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Carrier carrier2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (Carrier) returned2;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; scalarize(carrier2);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The bridge is acting as an ABI adapter. On one side is the erased &lt;code&gt;Object apply(Object)&lt;/code&gt; calling convention. On the other is the typed &lt;code&gt;Carrier apply(LargeValue)&lt;/code&gt; calling convention, where value components can be passed and returned in scalarized form.&lt;/p&gt;&#xA;&lt;p&gt;As you can imagine, this is very expensive. At each call, the caller has to turn a scalarized value into an object reference, which means materializing the value on the heap. It cannot simply point the callee at temporary stack storage: the call is opaque, so the callee may retain the reference and access it after the caller returns. The argument must therefore be a GC-managed heap object.&lt;/p&gt;&#xA;&lt;p&gt;Luckily, the fix is very simple! We avoid this by explicitly redeclaring the typed method in &lt;code&gt;Frobber&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Frobber&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;extends&lt;/span&gt; Fun&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Carrier, LargeValue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Override&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Carrier &lt;span style=&#34;color:#a6e22e&#34;&gt;apply&lt;/span&gt;(LargeValue value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;@Override&lt;/code&gt; annotation documents what we are doing, but the important part is the explicit method declaration. Calls whose static receiver type is &lt;code&gt;Frobber&lt;/code&gt; now use the typed descriptor directly:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;invokeinterface Frobber.apply:(LargeValue)Carrier&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The call is still megamorphic. C2 still does not know whether it will dispatch to &lt;code&gt;FrobIt&lt;/code&gt;, &lt;code&gt;GrobIt&lt;/code&gt;, or &lt;code&gt;DrobIt&lt;/code&gt;. But it no longer needs that knowledge to choose the correct calling convention. Every possible target accepts a &lt;code&gt;LargeValue&lt;/code&gt; and returns a &lt;code&gt;Carrier&lt;/code&gt;, so the values can cross the dynamic call boundary in scalarized form:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; CarrierFields &lt;span style=&#34;color:#a6e22e&#34;&gt;reproduce&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        LargeValueFields value,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Frobber a,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Frobber b) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CarrierFields c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            invokeinterface_typed_apply(a, value);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; invokeinterface_typed_apply(b, c.&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the reproducer, the erased megamorphic version allocated 192 bytes per invocation of &lt;code&gt;reproduce&lt;/code&gt;. Explicitly redeclaring the typed method reduced that to zero.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&#xA;&lt;/h2&gt;&lt;p&gt;Declaring a value class is first and foremost a semantic decision. It tells our fellow programmers that its instances are defined entirely by their state and do not need identity. That clearer model is valuable in itself! The JVM’s additional freedom to optimize how those values are represented is a welcome bonus.&lt;/p&gt;&#xA;&lt;p&gt;C2 can do amazing things with that freedom, but it cannot always recover information hidden behind abstraction boundaries. Profiling and inspecting the generated code remain the best ways to understand what is happening.&lt;/p&gt;&#xA;&lt;p&gt;To get the best results, we may still need to have a little sympathy for the compiler.&lt;/p&gt;&#xA;&lt;h2 id=&#34;appendix-printing-c2-assembly&#34;&gt;Appendix: printing C2 assembly&#xA;&lt;/h2&gt;&lt;p&gt;If you want to double-check my work, you can take these code snippets and inspect the assembly yourself. Compile with preview enabled, make sure the target method is invoked often enough to become hot, and then ask the VM to compile and print it:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;javac --enable-preview --release 28 Example.java&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java --enable-preview -Xbatch -XX:-TieredCompilation \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UnlockDiagnosticVMOptions \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:CompileCommand=compileonly,Example::method \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:CompileCommand=print,Example::method \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+PrintAssembly Example&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;-Xbatch&lt;/code&gt; makes compilation synchronous, and &lt;code&gt;compileonly&lt;/code&gt; keeps the output focused. Note that &lt;code&gt;compileonly&lt;/code&gt; restricts which methods may be compiled; it does not trigger compilation. &lt;code&gt;Example::method&lt;/code&gt; must still be invoked enough times to reach the compilation threshold. Printing assembly requires a JVM build with a disassembler available. Omit &lt;code&gt;--enable-preview&lt;/code&gt; when compiling and running the ordinary-record comparison.&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
