<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aleksandr Tiurin</title>
    <description>The latest articles on DEV Community by Aleksandr Tiurin (@aleksandr_tyurin_0592c37b).</description>
    <link>https://dev.to/aleksandr_tyurin_0592c37b</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3230398%2Fa39a50f5-9c78-4e14-9e26-615a7befe6a0.jpeg</url>
      <title>DEV Community: Aleksandr Tiurin</title>
      <link>https://dev.to/aleksandr_tyurin_0592c37b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aleksandr_tyurin_0592c37b"/>
    <language>en</language>
    <item>
      <title>Project Valhalla: Fundamental Changes in the JVM</title>
      <dc:creator>Aleksandr Tiurin</dc:creator>
      <pubDate>Tue, 03 Feb 2026 14:03:02 +0000</pubDate>
      <link>https://dev.to/aleksandr_tyurin_0592c37b/project-valhalla-fundamental-changes-in-the-jvm-3bih</link>
      <guid>https://dev.to/aleksandr_tyurin_0592c37b/project-valhalla-fundamental-changes-in-the-jvm-3bih</guid>
      <description>&lt;p&gt;&lt;em&gt;In my 12 years of Java development in fintech, I have not seen a project that promises such a radical improvement in solving our everyday problems. We, programmer, spend thousands of person-hours optimizing code, but everything ultimately runs into a fundamental limitation of Java: the cost of objects. Valhalla is a direct response to this pain. In this article, I will break down how it will change the way we process data, using an example that may be familiar to many of us — XML parsing.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Is Valhalla a Practical Lifesaver for Data-Intensive Code?
&lt;/h2&gt;

&lt;p&gt;Every OpenJDK project addresses a systemic problem. Valhalla is the answer to an &lt;strong&gt;architectural contradiction&lt;/strong&gt; we have tolerated for decades: to get type safety and abstractions, we are forced to pay in memory and CPU time by creating objects for every chunk of data. In large systems, where 80% of the workload is transforming and validating streams of structured data, this price becomes unacceptable.&lt;/p&gt;

&lt;p&gt;I sometimes have to work with parsing financial XML. Yes, XML is not as popular today, but it firmly occupies its niches — for example, XHTML, SOAP, and various configurations (such as Tomcat, SoapUI, and others), as well as data import/export.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Parsing Financial XML
&lt;/h3&gt;

&lt;p&gt;Let’s take a typical scenario — parsing an account statement in ISO 20022 format (&lt;a href="https://www.iso20022.org" rel="noopener noreferrer"&gt;https://www.iso20022.org&lt;/a&gt;). One file contains thousands of transactions (&lt;code&gt;Ntry&lt;/code&gt;). Each transaction is a cascade of objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Ntry&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Amt&lt;/span&gt; &lt;span class="na"&gt;Ccy=&lt;/span&gt;&lt;span class="s"&gt;"USD"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;1000.50&lt;span class="nt"&gt;&amp;lt;/Amt&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;CdtDbtInd&amp;gt;&lt;/span&gt;CRDT&lt;span class="nt"&gt;&amp;lt;/CdtDbtInd&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Sts&amp;gt;&lt;/span&gt;BOOK&lt;span class="nt"&gt;&amp;lt;/Sts&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;BookgDt&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Dt&amp;gt;&lt;/span&gt;2026-01-01&lt;span class="nt"&gt;&amp;lt;/Dt&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/BookgDt&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ValDt&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Dt&amp;gt;&lt;/span&gt;2026-01-01&lt;span class="nt"&gt;&amp;lt;/Dt&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ValDt&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/Ntry&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Traditional Java Model:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MonetaryAmount&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// First problem&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Currency&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Second problem&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EntryDetails&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;entryDetails&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Third problem&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MonetaryAmount&lt;/span&gt; &lt;span class="n"&gt;amt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;bookgDt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// LocalDate is being considered as a value class in future JDK versions!&lt;/span&gt;
    &lt;span class="c1"&gt;// + other fields&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What’s wrong here from a performance perspective:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BigDecimal&lt;/code&gt; is a performance killer&lt;/strong&gt; — internally it uses an &lt;code&gt;int[]&lt;/code&gt; array, which is a separate object with its own header&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Currency&lt;/code&gt; is redundant:&lt;/strong&gt; Most of the time it’s a constant from a pool, but we still store a reference to it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cascading allocations:&lt;/strong&gt; One &lt;code&gt;Transaction&lt;/code&gt; &amp;gt; 3–5 new heap objects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory fragmentation:&lt;/strong&gt; &lt;code&gt;MonetaryAmount&lt;/code&gt; objects are scattered across the heap, destroying locality&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When processing 100K transactions, &lt;strong&gt;300–500 thousand temporary objects&lt;/strong&gt; are created. The GC is pushed to its limits, and the CPU idles, waiting for cache misses while walking this reference graph.&lt;/p&gt;




&lt;h2&gt;
  
  
  Value Types in Action
&lt;/h2&gt;

&lt;p&gt;As stated in JEP 401, the project gives developers the ability to explicitly choose which objects need identity and which do not. Value classes are that choice: object semantics with primitive-like storage and performance.&lt;/p&gt;

&lt;p&gt;Let’s move to a &lt;strong&gt;Valhalla-based model:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Value class for monetary amounts&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MonetaryAmount&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Primitive instead of BigDecimal&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// String remains a reference type&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MonetaryAmount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;valueStr&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parseToMinorUnits&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valueStr&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;MonetaryAmount&lt;/span&gt; &lt;span class="n"&gt;amt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// Flattened embedding&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EntryDetails&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ntryDtls&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical change:&lt;/strong&gt; &lt;code&gt;MonetaryAmount&lt;/code&gt; can now be &lt;em&gt;flattened&lt;/em&gt; by the JVM into the memory layout of &lt;code&gt;Transaction&lt;/code&gt;, eliminating an extra level of indirection wherever possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Means at the CPU and Memory Level
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before (Classic Java):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction[] array:
[ref1, ref2, ...]
 |       |       
 \       \       
[Object1][Object2]...
 In each object:
 [header][refToAmount][refToDetails]
          |             |           
          \             \           
     [BigDecimal]  [ArrayList]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After (With Valhalla):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction[] array (flat representation):
[header1][amount1.value][amount1.currency][refToDetails1]
[header2][amount2.value][amount2.currency][refToDetails2]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A value object has no identity. It does not have its own unique memory address like an entity. For a parser, this yields measurable advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;60–70% fewer allocations:&lt;/strong&gt; Instead of 5 objects per transaction — 1–2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3–5× better data locality:&lt;/strong&gt; All transaction fields are stored sequentially&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An order-of-magnitude reduction in GC pressure:&lt;/strong&gt; Fewer objects → shorter and rarer pauses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;40% faster iteration:&lt;/strong&gt; The CPU gets all fields in a single cache line fetch&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Let’s Build a Practical Benchmark (Prototype on a Valhalla EA Build)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="nd"&gt;@BenchmarkMode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Throughput&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parseWithValhalla&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Blackhole&lt;/span&gt; &lt;span class="n"&gt;bh&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseXml&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FINANCIAL_XML_LARGE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Returns List&amp;lt;Transaction&amp;gt; with value classes&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;  
&lt;span class="nd"&gt;@BenchmarkMode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Throughput&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TransactionLegacy&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parseLegacy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Blackhole&lt;/span&gt; &lt;span class="n"&gt;bh&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseXmlLegacy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FINANCIAL_XML_LARGE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Classic objects&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why XML Parsing Benefits So Much From This Approach
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The “read–validate–accumulate” pattern&lt;/strong&gt; maps perfectly to value types. A StAX parser creates objects sequentially — exactly where flattening delivers maximum impact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutability by default&lt;/strong&gt; is already standard practice in financial models. Value classes simply formalize this at the JVM level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reducing GC pressure is critical&lt;/strong&gt; for near-real-time systems. Parsing a 100 MB XML file will no longer trigger 5-second garbage collection pauses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-processing becomes faster:&lt;/strong&gt; Once parsing is done and we start aggregating, filtering, and calculating — the data is already tightly packed, with no pointer chasing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower memory usage becomes an architectural effect:&lt;/strong&gt; By eliminating object headers and cascading allocations, the expected heap footprint is reduced by more than 2× compared to the classic object model.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  My Experiment: A StAX Parser With Valhalla
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Validate the hypothesis that switching financial models to value classes provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&amp;gt;50% fewer allocations&lt;/li&gt;
&lt;li&gt;&amp;gt;30% faster full pipeline (parsing + aggregation)&lt;/li&gt;
&lt;li&gt;Elimination of Young GC pauses for files &amp;gt;100 MB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Valhalla early-access build (latest)&lt;/li&gt;
&lt;li&gt;Standard StAX (&lt;code&gt;javax.xml.stream&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;JMH for benchmarking&lt;/li&gt;
&lt;li&gt;Real test data from a sandbox environment (sanctioned)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Experimental Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Value version of MonetaryAmount&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MonetaryAmount&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Stored in minor units (cents)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Preliminary Observations (Prototype):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replacing &lt;code&gt;BigDecimal&lt;/code&gt; with &lt;code&gt;long&lt;/code&gt; for minor units is the single most impactful optimization&lt;/li&gt;
&lt;li&gt;Nested value types (&lt;code&gt;Amount&lt;/code&gt; inside &lt;code&gt;Transaction&lt;/code&gt;) work as expected&lt;/li&gt;
&lt;li&gt;Compatibility with existing code: value classes can be used wherever an interface or class was expected&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What Will Need to Change in Real Projects?
&lt;/h2&gt;

&lt;p&gt;After practical testing of Valhalla on one of our XML parsing projects, achieving similar performance gains in other parts of the system will require some preparation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify value candidates in your models:&lt;/strong&gt; Look for immutable DTOs that are currently classic objects — &lt;code&gt;Amount&lt;/code&gt;, &lt;code&gt;Rate&lt;/code&gt;, &lt;code&gt;Coordinate&lt;/code&gt;, &lt;code&gt;Range&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rethink object pools:&lt;/strong&gt; Many pools (&lt;code&gt;ObjectPool&amp;lt;Transaction&amp;gt;&lt;/code&gt;) become unnecessary — value types can be created “on the stack” without GC pressure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prepare your libraries:&lt;/strong&gt; Verify that the libraries you use (Jackson for JSON, JAXB for XML) are ready to work with value classes. Oracle has already announced support in the Jakarta EE stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rewrite native optimizations:&lt;/strong&gt; Code with &lt;code&gt;byte[]&lt;/code&gt; buffers and manual serialization can be replaced with clean value types at the same performance level&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;After successfully accelerating a StAX parser, I realized that much more of the project can be migrated to Valhalla. Here are some examples of what that might look like.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. High-Frequency Market Data Parsing (FIX/FAST, XML)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Today&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MarketDataEvent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// With Valhalla  &lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Price&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;mantissa&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Decimal support via integers&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MarketDataEvent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Price&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Price&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Array of 1,000,000 events: previously 24 bytes of headers per event,&lt;/span&gt;
&lt;span class="c1"&gt;// now 0 bytes of headers for the value portion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Payment Validation With Transformation Chains
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidatedAmount&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isValid&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ValidatedAmount&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;targetCurrency&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getRate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetCurrency&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ValidatedAmount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetCurrency&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isValid&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Each convert() call creates no heap objects!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Caching Frequently Used Keys
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CurrencyPair&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Instead of 2 objects per key (String + String) + CurrencyPair header&lt;/span&gt;
&lt;span class="c1"&gt;// You get flat storage of characters inside the HashMap.Node block&lt;/span&gt;
&lt;span class="c1"&gt;// The JVM can store value keys without separate heap allocation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, not every class should become a value type. Entities with identity, stateful objects, and cache entries with TTL should remain classic classes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Valhalla is not “just another language feature.” It’s about &lt;strong&gt;finally being able to write expressive domain models without compromising on performance&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For developers, this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The end of the era of object pools and manual serialization&lt;/strong&gt; for optimization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ability to use clean DDD models&lt;/strong&gt; in high-throughput systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower system TCO&lt;/strong&gt; through reduced memory and GC requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java becoming competitive with C++ and Rust&lt;/strong&gt; in data processing, while retaining the full power of the JVM ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My verdict after experimenting with EA builds:&lt;/strong&gt; The technology works. The impact is visible even today. By the time it reaches production, we’ll have a solid set of patterns and libraries ready to unlock its benefits immediately. The final release will take time, but understanding these changes now is an investment in tomorrow.&lt;/p&gt;

&lt;p&gt;My repository with the experiment: &lt;a href="https://github.com/aotyurin/vl-xml-parser" rel="noopener noreferrer"&gt;GitLab: StAX Valhalla&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to Track Progress
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://openjdk.org/jeps/401" rel="noopener noreferrer"&gt;JEP 401: Value Classes&lt;/a&gt;&lt;/strong&gt; — the foundation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://jdk.java.net/valhalla/" rel="noopener noreferrer"&gt;Valhalla Early-Access Builds&lt;/a&gt;&lt;/strong&gt; — try it today&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://inside.java" rel="noopener noreferrer"&gt;Inside.java Newscast&lt;/a&gt;&lt;/strong&gt; — weekly updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If you’re not familiar with Valhalla, now is the perfect time to start experimenting.&lt;/strong&gt; The project is actively evolving. As of today, early-access builds with value class support are available (as listed on the project page).&lt;/p&gt;

&lt;p&gt;To get started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the Early-Access (EA) build from the Valhalla project page (&lt;a href="https://openjdk.org/projects/valhalla" rel="noopener noreferrer"&gt;https://openjdk.org/projects/valhalla&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Enable preview features when compiling and running:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;javac &lt;span class="nt"&gt;--enable-preview&lt;/span&gt; &lt;span class="nt"&gt;--source&lt;/span&gt; 24 Complex.java
java &lt;span class="nt"&gt;--enable-preview&lt;/span&gt; Complex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>java</category>
      <category>jvm</category>
      <category>performance</category>
    </item>
    <item>
      <title>No Magic: How Spring, Lombok, and Mockito Actually Work Under the JVM Hood</title>
      <dc:creator>Aleksandr Tiurin</dc:creator>
      <pubDate>Mon, 13 Oct 2025 19:44:18 +0000</pubDate>
      <link>https://dev.to/aleksandr_tyurin_0592c37b/no-magic-how-spring-lombok-and-mockito-actually-work-under-the-jvm-hood-476j</link>
      <guid>https://dev.to/aleksandr_tyurin_0592c37b/no-magic-how-spring-lombok-and-mockito-actually-work-under-the-jvm-hood-476j</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction: It’s Not Reflection—It’s Bytecode&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I’ve been developing in Java for several years—from small utilities to high-load services in distributed systems. In such systems, even a few seconds of downtime are unacceptable: we’re talking about SLAs of 99.99% and higher. To guarantee this level of reliability, I’ve had to do more than just write code—I’ve needed to deeply understand how it actually works inside the JVM.&lt;/p&gt;

&lt;p&gt;This led me to load testing, memory dump analysis, and profiling using agents like Async-Profiler and OpenTelemetry—and ultimately to the question: “How does Spring inject transactions without my involvement? Why can Mockito mock a class with no constructor? And how does Lombok ‘add’ methods that don’t exist in the source code?”&lt;/p&gt;

&lt;p&gt;I used to think it was magic too. But after diving into the architecture of the Java Virtual Machine (JVM), I realized everything is far more logical than it appears. In this article, we’ll walk step by step through how the JVM loads classes, verifies their correctness, and how you can modify their behavior at runtime.&lt;/p&gt;

&lt;p&gt;We’ll explore the full lifecycle of a class—from loading into the JVM to bytecode manipulation. We’ll understand how &lt;strong&gt;Class Loading&lt;/strong&gt; works, why &lt;strong&gt;verification&lt;/strong&gt; is necessary, and how tools like &lt;strong&gt;ASM&lt;/strong&gt;, &lt;strong&gt;Byte Buddy&lt;/strong&gt;, and &lt;strong&gt;Javassist&lt;/strong&gt; enable us to move from passive code consumption to active transformation. We’ll also examine how Lombok, Mockito, and Spring AOP leverage these mechanisms—without any magic at all.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;1. Class Loading: Delegation, Isolation, Customization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The process of loading a class into the JVM begins long before the first bytecode instruction is executed. This process, known as &lt;em&gt;Class Loading&lt;/em&gt;, is one of the earliest stages in a class’s lifecycle. The key component of this system is &lt;code&gt;java.lang.ClassLoader&lt;/code&gt;, an abstract class responsible for locating and loading the binary representation of a class (in &lt;code&gt;.class&lt;/code&gt; format) and converting it into an instance of &lt;code&gt;java.lang.Class&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The central principle of the ClassLoader architecture is the &lt;strong&gt;delegation model&lt;/strong&gt;. When a ClassLoader instance receives a request to load a class by name, it first delegates the request to its parent ClassLoader. Only if the parent cannot locate the class does the current ClassLoader attempt to load it itself. This hierarchy ensures security and prevents substitution of core system classes.&lt;/p&gt;

&lt;p&gt;The traditional hierarchy consists of three levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bootstrap ClassLoader (Primordial ClassLoader)&lt;/strong&gt;: sits at the top of the hierarchy and is part of the JVM itself. Implemented in native code (C/C++), it loads the most fundamental Java platform classes—those found in &lt;code&gt;rt.jar&lt;/code&gt; (or in modules starting with Java 9+), such as &lt;code&gt;java.lang.Object&lt;/code&gt;, &lt;code&gt;java.lang.String&lt;/code&gt;, and others. It cannot be directly accessed from user code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform ClassLoader (formerly Extension ClassLoader)&lt;/strong&gt;: introduced in Java 9+, it loads classes from platform modules. Prior to Java 9, this role belonged to the Extension ClassLoader, which loaded JAR files from the extension directory (&lt;code&gt;jre/lib/ext&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System ClassLoader (Application ClassLoader)&lt;/strong&gt;: this is the default ClassLoader for applications. It loads classes from paths specified in the &lt;code&gt;CLASSPATH&lt;/code&gt; environment variable, the &lt;code&gt;-classpath&lt;/code&gt; command-line argument, or the default directory. This is the ClassLoader used to load the application’s main class specified in the &lt;code&gt;java&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers can extend this model by creating custom &lt;code&gt;ClassLoader&lt;/code&gt; implementations. This is typically done by subclassing &lt;code&gt;ClassLoader&lt;/code&gt; and overriding one of its methods. The most common approach is to override the &lt;code&gt;findClass(String name)&lt;/code&gt; method. This preserves the standard delegation model (implemented in &lt;code&gt;loadClass(String name)&lt;/code&gt;) while allowing customization of how the class’s byte representation is located and retrieved.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;loadClass(String name)&lt;/code&gt; method serves as the entry point for class loading. It implements the delegation algorithm: first checking if the class has already been loaded (caching), then delegating to the parent, and only calling &lt;code&gt;findClass(name)&lt;/code&gt; if the class remains unfound. The &lt;code&gt;defineClass(byte[] b, int off, int len)&lt;/code&gt; method is a protected utility that actually converts a byte array into a &lt;code&gt;Class&lt;/code&gt; instance. It must be invoked from within &lt;code&gt;findClass&lt;/code&gt; or &lt;code&gt;loadClass&lt;/code&gt; after the bytes have been obtained. Importantly, &lt;code&gt;defineClass&lt;/code&gt; does not validate the bytecode—that responsibility falls to the subsequent &lt;em&gt;linking&lt;/em&gt; phase.&lt;/p&gt;

&lt;p&gt;Creating custom ClassLoaders has many practical applications but also carries risks. Poor implementations can lead to memory leaks. Additionally, each new ClassLoader creates an isolated namespace for classes, which may result in &lt;code&gt;ClassNotFoundException&lt;/code&gt; or &lt;code&gt;NoClassDefFoundError&lt;/code&gt; if dependencies aren’t properly resolved. Thus, working with ClassLoaders demands not only technical knowledge but also a deep understanding of application architecture.&lt;/p&gt;

&lt;p&gt;The idea of dynamically generating classes—once considered a niche technique—is now becoming part of the platform itself. The &lt;strong&gt;Unnamed Classes&lt;/strong&gt; feature introduced in the recently released Java 25 allows running Java script files (&lt;code&gt;.jsh&lt;/code&gt;), which are compiled on-the-fly into anonymous classes and loaded by the system ClassLoader via the &lt;code&gt;defineClass&lt;/code&gt; mechanism. This transforms what was once an advanced, specialized approach into a mainstream execution path for specific use cases.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2. Linking Phase: Verification, Preparation, Resolution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;After the &lt;code&gt;ClassLoader&lt;/code&gt; successfully loads the binary data of a class and creates an instance of &lt;code&gt;java.lang.Class&lt;/code&gt;, the class does not immediately become executable. Before its methods can be invoked or its fields accessed, the class must undergo the &lt;strong&gt;linking&lt;/strong&gt; process. This multi-stage procedure—defined in the JVM specification (JLS §5.4)—consists of three sequential phases: &lt;strong&gt;Verification&lt;/strong&gt;, &lt;strong&gt;Preparation&lt;/strong&gt;, and &lt;strong&gt;Resolution&lt;/strong&gt;. These steps are critical for ensuring the stability, security, and integrity of the Java runtime environment.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Verification: The Guardian of JVM Integrity&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Verification is the most complex and crucial phase of linking. Its purpose is to ensure that the class is safe and does not violate JVM invariants. It checks the structure of the &lt;code&gt;.class&lt;/code&gt; file, type safety, operand stack balance, correctness of references, and access control.&lt;/p&gt;

&lt;p&gt;The verification process is performed by internal JVM components such as &lt;code&gt;ClassFileParser&lt;/code&gt; and includes several levels of analysis:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Structural Verification&lt;/strong&gt; – validates the format of the &lt;code&gt;.class&lt;/code&gt; file, version numbers, constant pool size, etc.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bytecode Verification&lt;/strong&gt; – analyzes the bytecode stream of each method.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Starting with Java 7, bytecode verification was significantly optimized through &lt;strong&gt;stack map frames&lt;/strong&gt;—metadata inserted by the compiler into the &lt;code&gt;Code&lt;/code&gt; attribute that explicitly describes the types of local variables and the operand stack at key points in the code. This allows the verifier to perform a single-pass check, greatly accelerating application startup.&lt;/p&gt;

&lt;p&gt;Here is an example of invalid bytecode that would fail verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aload_0           // Push 'this' (an object reference) onto the stack
iconst_0          // Push int 0 onto the stack
invokevirtual java/io/PrintStream.println:(Ljava/lang/String;)V
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code attempts to call &lt;code&gt;println(String)&lt;/code&gt; with an &lt;code&gt;int&lt;/code&gt; argument, violating the method’s signature. The verifier will detect the type mismatch on the operand stack and throw a &lt;code&gt;java.lang.VerifyError&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Preparation: Memory Allocation for Static Members&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;During the &lt;strong&gt;Preparation&lt;/strong&gt; phase, the JVM allocates memory for all &lt;strong&gt;static fields&lt;/strong&gt; of the class. At this stage, fields are initialized &lt;strong&gt;only to their default values&lt;/strong&gt; (&lt;code&gt;0&lt;/code&gt; for numeric types, &lt;code&gt;false&lt;/code&gt; for &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt; for reference types). Initializers like &lt;code&gt;static int x = 5;&lt;/code&gt; are executed later—in the &lt;code&gt;&amp;lt;clinit&amp;gt;&lt;/code&gt; method (the static initializer).&lt;/p&gt;

&lt;p&gt;This phase also includes setting up internal JVM data structures used to represent the class, such as virtual method tables (vtables) and interface method tables (itables).&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Resolution: Resolving Symbolic References&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In this phase, the JVM converts symbolic references from the constant pool (class names, method names, field names) into direct references to actual runtime entities in memory. Resolution is typically performed &lt;strong&gt;lazily&lt;/strong&gt;—upon the first use of a reference.&lt;/p&gt;

&lt;p&gt;The main types of resolvable references include:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Classes and interfaces&lt;/strong&gt; – loaded by name;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fields&lt;/strong&gt; – located and access-checked;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Methods&lt;/strong&gt; – resolved by name and descriptor, with checks for overriding validity and accessibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any error occurs during resolution—for example, a class is missing, a method doesn’t exist, or access rules are violated—the JVM throws an appropriate error: &lt;code&gt;NoClassDefFoundError&lt;/code&gt;, &lt;code&gt;NoSuchFieldError&lt;/code&gt;, &lt;code&gt;NoSuchMethodError&lt;/code&gt;, &lt;code&gt;IllegalAccessError&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Once linking completes successfully, the class is technically ready for use—but its static initialization (&lt;code&gt;&amp;lt;clinit&amp;gt;&lt;/code&gt;) is deferred until the first active use of the class.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3. Bytecode and Class File Format: The Structure of .class&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Understanding class loading and linking is impossible without a deep dive into the very entity they operate on—the &lt;code&gt;.class&lt;/code&gt; file format. This binary format, strictly defined in the JVM Specification, serves as the universal carrier of compiled Java code. It is independent of the source language (Kotlin, Scala, Groovy also compile to this format) and the target platform’s architecture, enabling the famous "write once, run anywhere" paradigm. Examining its structure reveals exactly how the JVM interprets and verifies code.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;ClassFile Structure&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;.class&lt;/code&gt; file begins with the magic number &lt;code&gt;0xCAFEBABE&lt;/code&gt;, followed by version information, the constant pool, and class metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;ClassFile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;u4&lt;/span&gt; &lt;span class="n"&gt;magic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;u2&lt;/span&gt; &lt;span class="n"&gt;minor_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;major_version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;cp_info&lt;/span&gt; &lt;span class="n"&gt;constant_pool&lt;/span&gt;&lt;span class="p"&gt;[...];&lt;/span&gt;
    &lt;span class="n"&gt;u2&lt;/span&gt; &lt;span class="n"&gt;access_flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;this_class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;super_class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;u2&lt;/span&gt; &lt;span class="n"&gt;interfaces&lt;/span&gt;&lt;span class="p"&gt;[...];&lt;/span&gt;
    &lt;span class="n"&gt;field_info&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[...];&lt;/span&gt;
    &lt;span class="n"&gt;method_info&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;[...];&lt;/span&gt;
    &lt;span class="n"&gt;attribute_info&lt;/span&gt; &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;[...];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key components:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;minor_version&lt;/code&gt;, &lt;code&gt;major_version&lt;/code&gt;&lt;/strong&gt;: define the file format version. For example, &lt;code&gt;major_version=52&lt;/code&gt; corresponds to Java 8. The JVM will refuse to load classes with a higher version.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;constant_pool&lt;/code&gt;&lt;/strong&gt;: the central repository for class names, method names, strings, and type descriptors. All other structures reference it by index.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fields&lt;/code&gt;, &lt;code&gt;methods&lt;/code&gt;&lt;/strong&gt;: described by name, descriptor, and attributes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;attributes&lt;/code&gt;&lt;/strong&gt;: optional data sections. The most important include:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Code&lt;/code&gt;: contains the method’s bytecode, max stack size, local variable count, and exception handling table.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LineNumberTable&lt;/code&gt; / &lt;code&gt;LocalVariableTable&lt;/code&gt;: used for debugging.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RuntimeVisibleAnnotations&lt;/code&gt;: stores annotations accessible via reflection.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Bytecode: The Language of a Stack Machine&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Bytecode is a set of instructions executed by the Java Virtual Machine. The JVM is a &lt;strong&gt;stack-based machine&lt;/strong&gt;, meaning most operations pass data through an operand stack rather than CPU registers.&lt;/p&gt;

&lt;p&gt;Each instruction consists of a single byte (the opcode) and, optionally, operands. Consider this method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its bytecode (as shown by &lt;code&gt;javap -c&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Code:
     0: iload_1
     1: iload_2
     2: iadd
     3: ireturn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;iload_1&lt;/code&gt;: loads the value from local variable index 1 (&lt;code&gt;a&lt;/code&gt;) onto the operand stack.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iload_2&lt;/code&gt;: loads the value from local variable index 2 (&lt;code&gt;b&lt;/code&gt;) onto the stack.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iadd&lt;/code&gt;: pops two &lt;code&gt;int&lt;/code&gt; values from the stack, adds them, and pushes the result back.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ireturn&lt;/code&gt;: pops the &lt;code&gt;int&lt;/code&gt; result from the stack and returns it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This example shows how the high-level expression &lt;code&gt;a + b&lt;/code&gt; is translated into a sequence of stack operations. During the linking phase, the verifier must ensure the operand stack is always in a consistent state—for instance, that exactly two &lt;code&gt;int&lt;/code&gt; values are present before &lt;code&gt;iadd&lt;/code&gt; executes.&lt;/p&gt;

&lt;p&gt;Understanding the &lt;code&gt;.class&lt;/code&gt; file structure and bytecode semantics is an absolute prerequisite for any code manipulation. Tools like ASM work directly with these low-level structures, reading and modifying byte streams in strict compliance with the format specification. Any intervention at this level requires full awareness of how changes will affect verification and subsequent execution.&lt;/p&gt;

&lt;p&gt;Language evolution continuously shapes the &lt;code&gt;.class&lt;/code&gt; format. For example, the introduction of &lt;strong&gt;Unnamed Variables (JEP 477)&lt;/strong&gt; (&lt;code&gt;var _ = ...&lt;/code&gt;) in Java 25 requires special handling by the compiler. Although the variable &lt;code&gt;_&lt;/code&gt; is unused, it must still appear in the &lt;code&gt;LocalVariableTable&lt;/code&gt; attribute of the &lt;code&gt;Code&lt;/code&gt; section—but with special flags indicating its "unnamed" status. This illustrates how even seemingly simple language features trigger changes in JVM’s low-level data structures, which must be correctly processed and verified.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4. Bytecode Manipulation: ASM, Byte Buddy, Javassist&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In previous sections, we examined how the JVM consumes bytecode. Now it’s time to move to the next level—active creation and modification of this code. Bytecode manipulation is a powerful technique that allows programmatic alteration of class behavior before or during execution. This isn’t just “reflection on steroids”—it’s direct intervention into the essence of executable code. Specialized frameworks exist for this purpose, each offering its own level of abstraction and approach.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;ASM: Low-Level Control&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;ASM is the most popular and performant bytecode manipulation framework. It operates at the lowest possible level, providing direct access to bytecode instructions and &lt;code&gt;.class&lt;/code&gt; file structures. ASM uses the Visitor pattern, where &lt;code&gt;ClassVisitor&lt;/code&gt;, &lt;code&gt;MethodVisitor&lt;/code&gt;, and other components traverse class elements, allowing developers to insert, remove, or replace instructions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ClassWriter&lt;/span&gt; &lt;span class="n"&gt;cw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ClassWriter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ClassWriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;COMPUTE_FRAMES&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;ClassVisitor&lt;/span&gt; &lt;span class="n"&gt;cv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyClassVisitor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cw&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// When visiting the 'toString' method&lt;/span&gt;
&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;MethodVisitor&lt;/span&gt; &lt;span class="nf"&gt;visitMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;MethodVisitor&lt;/span&gt; &lt;span class="n"&gt;mv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"toString"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AddLoggingAdviceAdapter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Insert logging&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Adapter that adds code before and after the method&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddLoggingAdviceAdapter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AdviceAdapter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;AddLoggingAdviceAdapter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MethodVisitor&lt;/span&gt; &lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ASM9&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMethodEnter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitFieldInsn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;GETSTATIC&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"java/lang/System"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"out"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Ljava/io/PrintStream;"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitLdcInsn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entering toString()"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitMethodInsn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;INVOKEVIRTUAL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"java/io/PrintStream"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"println"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"(Ljava/lang/String;)V"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros of ASM&lt;/strong&gt;: Maximum flexibility and performance, minimal overhead.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Complex API, requires deep understanding of bytecode and stack map frames. A stack management error will cause a &lt;code&gt;VerifyError&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Byte Buddy: A Modern DSL over ASM&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Byte Buddy positions itself as a modern alternative to CGLIB and a more convenient wrapper over ASM. It provides a high-level, type-safe DSL that lets you describe transformations almost as if writing Java, hiding the complexities of bytecode manipulation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ByteBuddy&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;redefine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"toString"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;intercept&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LoggingInterceptor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;make&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getClassLoader&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;ClassLoadingStrategy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WRAPPER&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;@Advice.OnMethodEnter&lt;/code&gt; in &lt;code&gt;LoggingInterceptor&lt;/code&gt; is automatically converted into the corresponding bytecode instructions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros of Byte Buddy&lt;/strong&gt;: Clean, readable API; excellent support for new Java features (modules, records, sealed classes); powerful dynamic proxy capabilities.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Depends on ASM; some overhead due to abstractions.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Javassist: Source-Like Code Manipulation&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Javassist (Java Programming Assistant) offers the highest level of abstraction. It allows developers to write modifications as strings of Java source code, which are then compiled into bytecode on the fly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CtClass&lt;/span&gt; &lt;span class="n"&gt;ctClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ClassPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDefault&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.example.User"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;CtMethod&lt;/span&gt; &lt;span class="n"&gt;ctMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDeclaredMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"toString"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ctMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertBefore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{ System.out.println(\"Entering toString\"); }"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;modifiedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toBytecode&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros of Javassist&lt;/strong&gt;: Extremely easy to use, especially for quick PoCs.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Least performant, limited flexibility, may produce suboptimal bytecode. Not always compatible with the latest JVM versions.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Choosing the Right Tool&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The choice depends on the task:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ASM&lt;/strong&gt;: When maximum performance and full control are required (e.g., APM agents).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Byte Buddy&lt;/strong&gt;: For most modern tasks, especially when support for new standards and code readability matter.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Javassist&lt;/strong&gt;: For simple tasks or when rapid implementation is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these tools generate bytecode that must pass JVM verification. Their effectiveness lies in “knowing the rules” and correctly generating &lt;code&gt;stack map frames&lt;/code&gt; to avoid &lt;code&gt;VerifyError&lt;/code&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;5. Runtime Instrumentation: java.lang.instrument and JVMTI&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;So far, we’ve treated bytecode manipulation as a process that precedes or coincides with class loading. However, the most powerful and flexible capabilities emerge when changes are applied &lt;strong&gt;at runtime&lt;/strong&gt;, without restarting the application. This is achieved through two primary mechanisms: the high-level &lt;code&gt;java.lang.instrument&lt;/code&gt; API and the low-level JNI-based JVMTI (JVM Tool Interface).&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;java.lang.instrument&lt;/code&gt;: Dynamic Transformation&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;java.lang.instrument&lt;/code&gt; package, introduced in Java 5, provides a standard way for agents to attach to the JVM and modify bytecode on the fly. An agent is a special JAR file attached to the JVM either at startup or dynamically during execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Premain Agent (&lt;code&gt;-javaagent&lt;/code&gt;)&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
An agent attached at JVM startup using the &lt;code&gt;-javaagent:my-agent.jar&lt;/code&gt; flag. It must contain a &lt;code&gt;public static void premain(String agentArgs, Instrumentation inst)&lt;/code&gt; method. At this stage, you can register a &lt;code&gt;ClassFileTransformer&lt;/code&gt;, which is invoked before each class is loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyAgent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;premain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;agentArgs&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instrumentation&lt;/span&gt; &lt;span class="n"&gt;inst&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;inst&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTransformer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyClassFileTransformer&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClassFileTransformer&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ClassFileTransformer&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ClassLoader&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                            &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;classBeingRedefined&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                            &lt;span class="nc"&gt;ProtectionDomain&lt;/span&gt; &lt;span class="n"&gt;protectionDomain&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;classfileBuffer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IllegalClassFormatException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Modify classfileBuffer using ASM/Byte Buddy&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com/example/Service"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;modifyWithByteBuddy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classfileBuffer&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// No modification&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Agent-main (&lt;code&gt;agentmain&lt;/code&gt;) and Dynamic Loading&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Starting with Java 6, agents can be loaded dynamically into a running JVM using &lt;code&gt;com.sun.tools.attach.VirtualMachine&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;VirtualMachine&lt;/span&gt; &lt;span class="n"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VirtualMachine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1234"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// PID of the process&lt;/span&gt;
&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadAgent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/path/to/my-agent.jar"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"optional args"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;detach&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this, the agent must have a &lt;code&gt;public static void agentmain(String agentArgs, Instrumentation inst)&lt;/code&gt; method. This mechanism is used by many profilers (e.g., YourKit, Async-Profiler) and monitoring systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key limitation of &lt;code&gt;java.lang.instrument&lt;/code&gt;&lt;/strong&gt;: You can add fields and methods or modify method bodies, but you cannot delete or change the signatures of existing fields/methods after a class has been loaded. Changes apply only to new classes or via &lt;code&gt;retransformClasses()&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;JVMTI: Native Control over the JVM&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;JVMTI (JVM Tool Interface) is a native (C/C++) interface that provides the highest level of control over a running JVM. It succeeds older interfaces like JPDA (Java Platform Debugger Architecture). Through JVMTI, you can not only transform but also &lt;strong&gt;redefine&lt;/strong&gt; and &lt;strong&gt;retransform&lt;/strong&gt; already-loaded classes, and obtain detailed information about memory, threads, garbage collection, etc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;RedefineClasses&lt;/code&gt;&lt;/strong&gt;: Replaces the implementation of one or more already-loaded classes with new bytecode versions. Restrictions: you cannot change method signatures, inheritance hierarchy, or fields.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;RetransformClasses&lt;/code&gt;&lt;/strong&gt;: Forces the JVM to re-apply &lt;code&gt;ClassFileTransformer&lt;/code&gt; logic even to already-loaded classes, enabling new transformation rules to be applied globally.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified C example&lt;/span&gt;
&lt;span class="n"&gt;jvmtiError&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jvmti&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;RedefineClasses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jvmti&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;classDefinition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Where is JVMTI used?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interactive debuggers (e.g., IntelliJ IDEA Debugger).
&lt;/li&gt;
&lt;li&gt;Production profilers (YourKit, JProfiler) that collect performance data with minimal overhead.
&lt;/li&gt;
&lt;li&gt;HotSwap tools (though standard IDE HotSwap is limited).
&lt;/li&gt;
&lt;li&gt;Security and monitoring systems requiring deep introspective access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JVMTI is significantly more complex and dangerous than &lt;code&gt;java.lang.instrument&lt;/code&gt;. An error in native code can crash the entire JVM. However, it provides unprecedented control, essential for professional diagnostic tools. These mechanisms demonstrate that the JVM is not just a runtime environment but an open platform for metaprogramming—where program behavior can be altered externally based on its internal state.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;6. How Lombok, Spring, and Mockito Work&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The theory of bytecode manipulation becomes truly tangible when we examine its use in widely adopted frameworks and libraries. These tools don’t rely on “magic”—they leverage deep JVM mechanisms we’ve already discussed. Understanding their internals reveals the practical value of knowledge about Class Loading and bytecode manipulation.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Lombok: Code Generation Behind the Scenes&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Project Lombok is known for adding methods (&lt;code&gt;toString&lt;/code&gt;, &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;getter&lt;/code&gt;, &lt;code&gt;setter&lt;/code&gt;) via annotations. Its implementation combines two approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Annotation Processing (AP)&lt;/strong&gt;: At compile time, when &lt;code&gt;javac&lt;/code&gt; processes source code, Lombok registers its own &lt;code&gt;Processor&lt;/code&gt;. This processor inspects the AST (Abstract Syntax Tree) and inserts nodes for missing methods directly into the syntax tree before bytecode is generated. This occurs within the standard compilation process.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Javac Internals / Instrumentation&lt;/strong&gt;: For certain features (e.g., &lt;code&gt;@SneakyThrows&lt;/code&gt;) or IDE integration, Lombok directly interacts with &lt;code&gt;javac&lt;/code&gt;’s internal APIs (package &lt;code&gt;com.sun.tools.javac.*&lt;/code&gt;) to modify the AST. At runtime, in some configurations, an agent (&lt;code&gt;-javaagent&lt;/code&gt;) may be used for introspection, but the core work happens at compile time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;: Lombok doesn’t generate separate &lt;code&gt;.class&lt;/code&gt; files—it modifies the compilation of the current file. The resulting bytecode is identical to what would be produced if the methods were written manually, so it passes verification effortlessly.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Spring AOP and CGLIB: Proxying Without Interfaces&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The Spring Framework heavily uses Aspect-Oriented Programming (AOP) to inject cross-cutting concerns (transaction management, security, logging). When a bean doesn’t implement an interface but a proxy is needed, Spring uses CGLIB.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CGLIB (Code Generation Library)&lt;/strong&gt;: This library (built on ASM) dynamically creates a subclass of the target class at runtime.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mechanism&lt;/strong&gt;: CGLIB analyzes the original class’s bytecode, generates a new class that extends it, and overrides all &lt;code&gt;public&lt;/code&gt; and &lt;code&gt;protected&lt;/code&gt; methods. In the overridden methods, it inserts calls to a &lt;code&gt;Callback&lt;/code&gt; (e.g., &lt;code&gt;MethodInterceptor&lt;/code&gt;) that manages the original method invocation and aspect execution.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pseudocode of what CGLIB generates&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;$EnhancerByCGLIB&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Callback&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;intercept&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saveMethod&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This subclass is generated in memory, its bytecode is passed to &lt;code&gt;ClassLoader#defineClass&lt;/code&gt;, and an instance of this class is used instead of the original. This approach allows Spring to apply AOP to any class—but requires that the class and its methods are not &lt;code&gt;final&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Mockito: Creating Mocks Out of Thin Air&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Mockito—one of the most popular testing libraries—also relies on bytecode manipulation. When you write &lt;code&gt;mock(MyUserService.class)&lt;/code&gt;, Mockito must create an object that:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is an instance of &lt;code&gt;MyUserService&lt;/code&gt; (or its subclass),
&lt;/li&gt;
&lt;li&gt;Doesn’t execute real logic,
&lt;/li&gt;
&lt;li&gt;Can record calls and return stubs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To achieve this, Mockito uses &lt;strong&gt;Objenesis&lt;/strong&gt; and &lt;strong&gt;Byte Buddy&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instance creation&lt;/strong&gt;: Objenesis uses low-level JVM capabilities (often via &lt;code&gt;sun.misc.Unsafe.allocateInstance()&lt;/code&gt;) to instantiate a class without invoking its constructor.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy generation&lt;/strong&gt;: Byte Buddy generates a subclass of the mocked class. All method calls on the mock are intercepted, recorded, and handled according to configured behavior (e.g., &lt;code&gt;when(...).thenReturn(...)&lt;/code&gt;). This entire process occurs at runtime during test execution and is fully based on &lt;code&gt;ClassLoader&lt;/code&gt; and &lt;code&gt;Instrumentation&lt;/code&gt; capabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Observability Agents (OpenTelemetry, Micrometer)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Modern APM systems attach as agents (&lt;code&gt;-javaagent&lt;/code&gt;) and register a &lt;code&gt;ClassFileTransformer&lt;/code&gt;. When classes matching certain patterns are loaded (e.g., methods annotated with &lt;code&gt;@RequestMapping&lt;/code&gt;, &lt;code&gt;@Timed&lt;/code&gt;, or HTTP client entry points), the agent modifies their bytecode to insert calls to tracing or metrics SDKs. This enables telemetry collection without modifying the application’s source code.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion: Power and Responsibility&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We’ve journeyed from class loading to runtime modification.&lt;br&gt;&lt;br&gt;
We’ve seen how verification protects the JVM, how &lt;code&gt;.class&lt;/code&gt; stores code, and how ASM, Byte Buddy, and JVMTI grant us control over it.&lt;/p&gt;

&lt;p&gt;These mechanisms form the foundation of the modern Java ecosystem. Without them, tools like Spring (with its proxying and dependency injection), Mockito (with its flexible mocks), Lombok (with its concise annotations), or powerful APM systems (which provide deep performance insights without code changes) wouldn’t exist. They transform Java from a static language into a platform rich with metaprogramming capabilities.&lt;/p&gt;

&lt;p&gt;However, with great power comes great responsibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Every runtime intervention takes time. A poorly implemented &lt;code&gt;ClassFileTransformer&lt;/code&gt; can slow application startup by orders of magnitude.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity and Debugging&lt;/strong&gt;: Errors in generated bytecode lead to &lt;code&gt;VerifyError&lt;/code&gt; or &lt;code&gt;IllegalAccessError&lt;/code&gt;, which are extremely hard to diagnose. Stack traces lose their connection to source code.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Leaks&lt;/strong&gt;: Custom ClassLoaders, if not properly released, retain all their loaded classes and static data in memory, causing metaspace leaks.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility&lt;/strong&gt;: Tools written for one JVM version may break in another.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: The ability to modify any class opens the door to serious vulnerabilities if the agent or instrumentation isn’t trusted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nevertheless, understanding these mechanisms is essential for any developer aiming to go beyond boilerplate programming. It enables you to:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deeply understand the frameworks you use,
&lt;/li&gt;
&lt;li&gt;Diagnose complex class-loading and performance issues,
&lt;/li&gt;
&lt;li&gt;Build your own tools for monitoring, testing, and extending functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;P.S. Have you encountered memory leaks due to custom ClassLoaders? Or perhaps written your own monitoring agent? Share your experience in the comments—it will be valuable for everyone!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>java</category>
      <category>performance</category>
    </item>
    <item>
      <title>How I Deployed a Java Spring Boot + Angular SSR Website Using Docker and Nginx: A Personal Journey</title>
      <dc:creator>Aleksandr Tiurin</dc:creator>
      <pubDate>Sat, 31 May 2025 11:16:03 +0000</pubDate>
      <link>https://dev.to/aleksandr_tyurin_0592c37b/how-i-deployed-a-java-spring-boot-angular-ssr-website-using-docker-and-nginx-a-personal-journey-4e4b</link>
      <guid>https://dev.to/aleksandr_tyurin_0592c37b/how-i-deployed-a-java-spring-boot-angular-ssr-website-using-docker-and-nginx-a-personal-journey-4e4b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a real-world story — with challenges, solutions, and valuable lessons that may benefit other developers or serve as a reference for myself in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The project is a web application built with Spring Boot and Angular. Initially, I went with a simple monolithic architecture, but as the requirements and ambitions grew, I had to switch to a microservices-based structure. Here's how it all happened — including configurations, issues, and solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monolithic Architecture
&lt;/h2&gt;

&lt;p&gt;Initial setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend: Spring Boot&lt;/li&gt;
&lt;li&gt;Frontend: Angular
I already had a rented VDS, so I registered a domain via a well-known provider and decided to host the site on that VDS. To keep things simple, I integrated the Angular build into the Spring Boot resources using frontend-maven-plugin, bundling everything into a single JAR file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why? I had done it before and it worked, so I chose the familiar path.&lt;/p&gt;

&lt;p&gt;After registering the domain (which takes some time to propagate), I began configuring and testing the project. But during testing, major drawbacks surfaced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No SSR in Angular → SEO was negatively impacted&lt;/li&gt;
&lt;li&gt;Fragile build: Angular errors broke Maven build&lt;/li&gt;
&lt;li&gt;Maintenance and scalability became difficult&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Moving to Microservices
&lt;/h2&gt;

&lt;p&gt;I decided to split the frontend and backend into separate services and introduce Angular Universal for SSR. Here's the updated architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend: Spring Boot&lt;/li&gt;
&lt;li&gt;Frontend: Angular Universal (SSR)&lt;/li&gt;
&lt;li&gt;Infrastructure: Docker Compose, Nginx, HTTPS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Docker, Nginx and Initial Launch
&lt;/h2&gt;

&lt;p&gt;Base docker-compose.yml setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  frontend-service:
    image: node:18-alpine
    command: node server/main.js

  backend-service:
    image: amazoncorretto:17-alpine-jdk
    command: java -jar app.jar

  nginx:
    image: nginx:alpine
    # Proxy configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initial Nginx config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    server_name my-site.ru;

    location / {
        proxy_pass http://frontend-service:9002;
    }

    location /api {
        proxy_pass http://backend-service:9001;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The site started and worked via IP, but there were many issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicated API prefixes (/api/api/endpoint)
→ Fixed proxy_pass and Angular API base URL&lt;/li&gt;
&lt;li&gt;Redirects on POST requests due to Spring adding extra slashes
→ Fixed via correct controller annotations and Nginx tuning&lt;/li&gt;
&lt;li&gt;405 Method Not Allowed
→ Fixed by explicitly using @PostMapping&lt;/li&gt;
&lt;li&gt;Docker networking issues
→ Resolved by defining a shared network in docker-compose.yml&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security: HTTPS, SSL, CORS
&lt;/h2&gt;

&lt;p&gt;To make the site accessible via domain name, I configured DNS records through my provider (they offer free DNS). I pointed @ and www to the VDS IP address.&lt;/p&gt;

&lt;p&gt;The domain provider also offered free SSL certificates (Let's Encrypt). Here's how I set them up: downloaded from the provider panel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;domain.crt — certificate&lt;/li&gt;
&lt;li&gt;domain.key — private key&lt;/li&gt;
&lt;li&gt;ca_bundle.crt — certificate chain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nginx HTTPS configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;listen 443 ssl;
ssl_certificate /etc/nginx/ssl/domain.crt;
ssl_certificate_key /etc/nginx/ssl/domain.key;
ssl_trusted_certificate /etc/nginx/ssl/ca_bundle.crt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, HTTPS failed to work initially. The Nginx logs showed &lt;code&gt;SSL_CTX_use_PrivateKey_file&lt;/code&gt; errors. The issue? The certificate was in PKCS#7 format, but Nginx requires PEM. I converted it using OpenSSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl pkcs7 -print_certs -in domain.p7b -out domain.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, since certificates expire, don't forget to renew them. Eventually, I plan to automate this (e.g. with Certbot).&lt;/p&gt;

&lt;p&gt;On the Spring Boot side, CORS errors were solved like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/api/**")
                    .allowedOrigins("https://example.com");
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bot Attacks and Protection
&lt;/h2&gt;

&lt;p&gt;Soon after launching the site, Nginx logs filled with automated bot requests probing for WordPress, PHPMyAdmin, etc. Example:&lt;br&gt;
&lt;code&gt;45.155.205.213 - - [01/Jan/2023:04:12:11 +0000] "GET /wp-login.php HTTP/1.1" 404 153&lt;/code&gt;&lt;br&gt;
Nginx protection rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;location ~* ^/(wp-admin|wp-login|phpmyadmin|.env|config.php) {
    return 403;
}

if ($request_method ~ ^(TRACE|TRACK|DEBUG)) {
    return 405;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Configuration
&lt;/h2&gt;

&lt;p&gt;After several iterations, here’s what my current setup looks like:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;docker-compose.yml&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.8'

networks:
  app-network:

services:
  backend-service:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: backend-service
    ports:
      - "9001:9001"
    volumes:
      - ./backend/logs:/app/logs
    env_file:
      - ./.env
    restart: always
    networks:
      - app-network

  frontend-service:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: frontend-service
    ports:
      - "9002:9002"
    environment:
      - NODE_ENV=production
    restart: always
    networks:
      - app-network

  proxy-service:
    image: nginx:alpine
    container_name: proxy-service
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d:ro
      - ./ssl-certs:/etc/nginx/ssl:ro
      - ./nginx/logs:/var/log/nginx
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - backend-service
      - frontend-service
    restart: always
    networks:
      - app-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;nginx.conf&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;worker_processes auto;

events {
    worker_connections 1024;
}

http {
    upstream frontend {
        server frontend-service:9002;
    }

    upstream backend {
        server backend-service:9001;
    }

    server {
        listen 80;
        server_name example.com www.example.com;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name example.com www.example.com;

        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;

        add_header Strict-Transport-Security "max-age=31536000" always;

        location / {
            proxy_pass http://frontend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        location /api/ {
            proxy_pass http://backend/api/;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }

        location ~* ^/(wp-admin|wp-login|phpmyadmin|.env|config.php) {
            return 403;
        }

        if ($request_method ~ ^(TRACE|TRACK|DEBUG)) {
            return 405;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;backend &lt;em&gt;Dockerfile&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM amazoncorretto:17-alpine-jdk
WORKDIR /app
COPY my-spring-1.0.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;frontend &lt;em&gt;Dockerfile&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:18-alpine AS runtime
WORKDIR /app
COPY browser /app/browser
COPY server /app/server
COPY node_modules /app/node_modules
EXPOSE 4000
CMD ["node", "/app/server/server.mjs"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The website is now fully up and running. Splitting services made it more maintainable and scalable. Hopefully, this post will help others going down a similar path with Java and Angular.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>java</category>
    </item>
  </channel>
</rss>
