<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div><br></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"Joe Mwangi" <joemwangimburu@gmail.com><br><b>To: </b>"valhalla-dev" <valhalla-dev@openjdk.org><br><b>Sent: </b>Sunday, October 26, 2025 2:30:52 PM<br><b>Subject: </b>Observations and Questions on Flattening Behavior and Memory Alignment in Value Objects<br></blockquote></div><div data-marker="__QUOTED_TEXT__"><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div dir="ltr"><div><p>Hi Valhalla Development Team,</p></div></div></blockquote><div><br></div><div>Hello Joe,</div><div>i'm not part of the implementation team (i'm part of the spec team), but i can answer to some of your questions,</div><div><br data-mce-bogus="1"></div><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div dir="ltr"><div><p>Thank you for providing the latest build for testing progress on value objects in the JVM. I’ve been running a few experiments to understand the behavior of custom value types and their memory characteristics, and I must say I’m very impressed by the implementation so far. Memory usage appears significantly reduced in many cases (sometimes to nearly ¾ of the original).</p><p>Here’s a simple test I used:</p><div class="gmail-contain-inline-size gmail-rounded-2xl gmail-relative gmail-bg-token-sidebar-surface-primary"><div class="gmail-overflow-y-auto gmail-p-4" dir="ltr"><code class="gmail-whitespace-pre! gmail-language-java"><span class="gmail-hljs-keyword">public</span> <span class="gmail-hljs-keyword">class</span> <span class="gmail-hljs-title gmail-class_">ValhallaTest</span> {
    value <span class="gmail-hljs-keyword">record</span> <span class="gmail-hljs-title gmail-class_">PointRecord</span><span class="gmail-hljs-params">(<span class="gmail-hljs-type">short</span></span> x, <span class="gmail-hljs-type">short</span> y, <span class="gmail-hljs-type">short</span> z) {}

    <span class="gmail-hljs-keyword">void</span> <span class="gmail-hljs-title gmail-function_">main</span><span class="gmail-hljs-params">()</span> <span class="gmail-hljs-keyword">throws</span> InterruptedException {
        Thread.sleep(<span class="gmail-hljs-number">9000</span>); <span class="gmail-hljs-comment">// allow time to attach VisualVM</span>
        System.out.println(<span class="gmail-hljs-string">"Starting"</span>);
        <span class="gmail-hljs-type">int</span> <span class="gmail-hljs-variable">size</span> <span class="gmail-hljs-operator">=</span> <span class="gmail-hljs-number">10_000_000</span>;
        <span class="gmail-hljs-type">var</span> <span class="gmail-hljs-variable">pointRecords</span> <span class="gmail-hljs-operator">=</span> <span class="gmail-hljs-keyword">new</span> <span class="gmail-hljs-title gmail-class_">PointRecord</span>[size];
        <span class="gmail-hljs-keyword">for</span> (<span class="gmail-hljs-type">int</span> <span class="gmail-hljs-variable">i</span> <span class="gmail-hljs-operator">=</span> <span class="gmail-hljs-number">0</span>; i < size; i++) {
            pointRecords[i] = <span class="gmail-hljs-keyword">new</span> <span class="gmail-hljs-title gmail-class_">PointRecord</span>((<span class="gmail-hljs-type">short</span>) <span class="gmail-hljs-number">2</span>, (<span class="gmail-hljs-type">short</span>) <span class="gmail-hljs-number">2</span>, (<span class="gmail-hljs-type">short</span>) <span class="gmail-hljs-number">3</span>);
        }
        Thread.sleep(<span class="gmail-hljs-number">20000</span>);
    }
}
</code></div></div><p>Using VisualVM, I inspected live objects and heap usage, with the following observations:</p><ol><li><p>No individual <code>PointRecord</code> objects were detected — only the <code>PointRecord[]</code> array, confirming full flattening (no identity objects).</p></li><li><p><code>PointRecord(short, short, short)</code> logically occupies 6 bytes, but the array reports <strong>80 000 000 B</strong> for 10 M elements → 8 bytes per element, suggesting alignment to 64 bits.</p></li></ol></div></div></blockquote><div><br></div><div><br></div><div>it's short + short + short + byte (for null) + alignment, hence 64 bits.<br><br></div><div dir="ltr"><div><ol><li><p><code>PointRecord(short x, short y)</code> → <strong>40 000 000 B</strong> → 4 bytes per element.</p></li><li><p><code>PointRecord(byte x)</code> → <strong>20 000 000 B</strong> → 2 bytes per element.</p></li></ol><p>> It appears the prototype aligns flattened array elements to the smallest power of two ≥ the logical size (not just 4-byte boundaries). Beyond 64 bits (≥ 8 bytes), flattening seems to stop, possibly reverting to identity semantics, which makes sense given mutation and tearing concerns.</p><p>So it's more than you need to add a bit (a byte) representing null and then it has to be a power of two (alignment) because you want to read the location in one read/write to avoid tearing.</p><p><br></p><p>> A few questions came up from these results:</p><ol><li><p>Will arrays need to be immutable to guarantee flattening for value elements larger than 64 bits? Think about parsing large files, where a large array with larger sized value object will be important, hence mutable array being key, and then do simd parsing.</p></li></ol></div></div><div><br></div><div>The array needs to have non-null elements, and you need a way to opt-out of atomicity (allow tearing).</div><div>The exact way to do the latter is still in flux.</div><div><br></div><div>About using simd, there is an issue because you can read/write using simd but then you need to extract the value from the simd register to a general purpose register and this is actually quite slow,</div><div>so the Hotspot does not do that.</div><div><br></div><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div dir="ltr"><div><ol><li></li><li><p>Since value objects are scalarized across stack calls, will there be tooling to analyze whether scalarization actually occurs (e.g., register-based limits determined by the JVM, if not enough register space, value object gains identity)?</p></li></ol></div></div></blockquote><div><br></div><div>as far as i know, c2 (the only JIT that optimize value classes) will scalarize depending on the size of the instance but not depending on if you have not enough registers, the registers will be spilled on stack in that case.</div><div><br></div><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div dir="ltr"><div><ol><li></li><li><p>In C, struct size can be predicted from field types. For Java value objects, since layout is JVM-dependent, is there a plan for tooling (perhaps <code>jcmd</code> or JFR integration) to expose explicit size/layout information beyond array inspection? The default above example shows that a 6 bytes size value object is actually 8 bytes, but in C, it shall remain 6 byte size.</p></li></ol></div></div></blockquote><div><br></div><div>you can use<span style="background-color: rgb(255, 255, 255);" data-mce-style="background-color: #ffffff;"> -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlineLayout but it only works for fields, not for arrays.</span></div><div><br></div><blockquote style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;" data-mce-style="border-left: 2px solid #1010FF; margin-left: 5px; padding-left: 5px; color: #000; font-weight: normal; font-style: normal; text-decoration: none; font-family: Helvetica,Arial,sans-serif; font-size: 12pt;"><div dir="ltr"><div><p>Overall, this is very exciting work. The model feels both efficient and semantically robust, offering a fresh take compared to languages that rely on purely compile-time memory determinism. I’ll continue exploring performance and GC interaction aspects later, but even this preliminary testing shows remarkable promise.</p><p>Thanks again to the entire team for the great work.</p><p>Kind regards,<br><strong>Joe Mwangi</strong></p></div></div></blockquote><div><br></div><div>regards,</div><div>Rémi</div><div><br></div></div></div></body></html>