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.
Is Valhalla a Practical Lifesaver for Data-Intensive Code?
Every OpenJDK project addresses a systemic problem. Valhalla is the answer to an architectural contradiction 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.
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.
The Problem: Parsing Financial XML
Let’s take a typical scenario — parsing an account statement in ISO 20022 format (https://www.iso20022.org). One file contains thousands of transactions (Ntry). Each transaction is a cascade of objects:
<Ntry>
<Amt Ccy="USD">1000.50</Amt>
<CdtDbtInd>CRDT</CdtDbtInd>
<Sts>BOOK</Sts>
<BookgDt>
<Dt>2026-01-01</Dt>
</BookgDt>
<ValDt>
<Dt>2026-01-01</Dt>
</ValDt>
...
</Ntry>
Traditional Java Model:
public class MonetaryAmount {
private final BigDecimal value; // First problem
private final Currency currency; // Second problem
}
public class Transaction {
private List<EntryDetails> entryDetails; // Third problem
private MonetaryAmount amt;
private LocalDate bookgDt; // LocalDate is being considered as a value class in future JDK versions!
// + other fields
}
What’s wrong here from a performance perspective:
-
BigDecimalis a performance killer — internally it uses anint[]array, which is a separate object with its own header -
Currencyis redundant: Most of the time it’s a constant from a pool, but we still store a reference to it -
Cascading allocations: One
Transaction> 3–5 new heap objects -
Memory fragmentation:
MonetaryAmountobjects are scattered across the heap, destroying locality
When processing 100K transactions, 300–500 thousand temporary objects are created. The GC is pushed to its limits, and the CPU idles, waiting for cache misses while walking this reference graph.
Value Types in Action
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.
Let’s move to a Valhalla-based model:
// Value class for monetary amounts
public value class MonetaryAmount {
private final long value; // Primitive instead of BigDecimal
private final String currency; // String remains a reference type
public MonetaryAmount(String valueStr, String currency) {
this.value = parseToMinorUnits(valueStr);
this.currency = currency;
}
}
public class Transaction {
private final MonetaryAmount amt; // Flattened embedding
private final List<EntryDetails> ntryDtls;
}
Critical change: MonetaryAmount can now be flattened by the JVM into the memory layout of Transaction, eliminating an extra level of indirection wherever possible.
What This Means at the CPU and Memory Level
Before (Classic Java):
Transaction[] array:
[ref1, ref2, ...]
| |
\ \
[Object1][Object2]...
In each object:
[header][refToAmount][refToDetails]
| |
\ \
[BigDecimal] [ArrayList]
After (With Valhalla):
Transaction[] array (flat representation):
[header1][amount1.value][amount1.currency][refToDetails1]
[header2][amount2.value][amount2.currency][refToDetails2]
...
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:
- 60–70% fewer allocations: Instead of 5 objects per transaction — 1–2
- 3–5× better data locality: All transaction fields are stored sequentially
- An order-of-magnitude reduction in GC pressure: Fewer objects → shorter and rarer pauses
- 40% faster iteration: The CPU gets all fields in a single cache line fetch
Let’s Build a Practical Benchmark (Prototype on a Valhalla EA Build)
@Benchmark
@BenchmarkMode(Mode.Throughput)
public List<Transaction> parseWithValhalla(Blackhole bh) {
return parseXml(FINANCIAL_XML_LARGE); // Returns List<Transaction> with value classes
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public List<TransactionLegacy> parseLegacy(Blackhole bh) {
return parseXmlLegacy(FINANCIAL_XML_LARGE); // Classic objects
}
Why XML Parsing Benefits So Much From This Approach
- The “read–validate–accumulate” pattern maps perfectly to value types. A StAX parser creates objects sequentially — exactly where flattening delivers maximum impact.
- Immutability by default is already standard practice in financial models. Value classes simply formalize this at the JVM level.
- Reducing GC pressure is critical for near-real-time systems. Parsing a 100 MB XML file will no longer trigger 5-second garbage collection pauses.
- Post-processing becomes faster: Once parsing is done and we start aggregating, filtering, and calculating — the data is already tightly packed, with no pointer chasing.
- Lower memory usage becomes an architectural effect: By eliminating object headers and cascading allocations, the expected heap footprint is reduced by more than 2× compared to the classic object model.
My Experiment: A StAX Parser With Valhalla
Goal: Validate the hypothesis that switching financial models to value classes provides:
- >50% fewer allocations
- >30% faster full pipeline (parsing + aggregation)
- Elimination of Young GC pauses for files >100 MB
Stack:
- Valhalla early-access build (latest)
- Standard StAX (
javax.xml.stream) - JMH for benchmarking
- Real test data from a sandbox environment (sanctioned)
Key Experimental Code:
// Value version of MonetaryAmount
public value class MonetaryAmount {
private final long value; // Stored in minor units (cents)
private final String currency;
}
Preliminary Observations (Prototype):
- Replacing
BigDecimalwithlongfor minor units is the single most impactful optimization - Nested value types (
AmountinsideTransaction) work as expected - Compatibility with existing code: value classes can be used wherever an interface or class was expected
What Will Need to Change in Real Projects?
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:
-
Identify value candidates in your models: Look for immutable DTOs that are currently classic objects —
Amount,Rate,Coordinate,Range -
Rethink object pools: Many pools (
ObjectPool<Transaction>) become unnecessary — value types can be created “on the stack” without GC pressure - Prepare your libraries: 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
-
Rewrite native optimizations: Code with
byte[]buffers and manual serialization can be replaced with clean value types at the same performance level
Real-World Use Cases
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.
1. High-Frequency Market Data Parsing (FIX/FAST, XML)
// Today
public class MarketDataEvent {
private final BigDecimal price;
private final BigDecimal size;
private final long timestamp;
}
// With Valhalla
public value class Price {
private final double mantissa;
private final int exponent; // Decimal support via integers
}
public value class MarketDataEvent {
private final Price price;
private final Price size;
private final long timestamp;
}
// Array of 1,000,000 events: previously 24 bytes of headers per event,
// now 0 bytes of headers for the value portion
2. Payment Validation With Transformation Chains
value class ValidatedAmount {
private final double value;
private final String currency;
private final boolean isValid;
public ValidatedAmount convert(String targetCurrency) {
double rate = getRate(this.currency, targetCurrency);
return new ValidatedAmount(this.value * rate, targetCurrency, this.isValid);
}
}
// Each convert() call creates no heap objects!
3. Caching Frequently Used Keys
public value class CurrencyPair {
private final String base;
private final String quote;
}
// Instead of 2 objects per key (String + String) + CurrencyPair header
// You get flat storage of characters inside the HashMap.Node block
// The JVM can store value keys without separate heap allocation
Of course, not every class should become a value type. Entities with identity, stateful objects, and cache entries with TTL should remain classic classes.
Conclusion
Valhalla is not “just another language feature.” It’s about finally being able to write expressive domain models without compromising on performance.
For developers, this means:
- The end of the era of object pools and manual serialization for optimization
- The ability to use clean DDD models in high-throughput systems
- Lower system TCO through reduced memory and GC requirements
- Java becoming competitive with C++ and Rust in data processing, while retaining the full power of the JVM ecosystem
My verdict after experimenting with EA builds: 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.
My repository with the experiment: GitLab: StAX Valhalla
Where to Track Progress
- JEP 401: Value Classes — the foundation
- Valhalla Early-Access Builds — try it today
- Inside.java Newscast — weekly updates
If you’re not familiar with Valhalla, now is the perfect time to start experimenting. The project is actively evolving. As of today, early-access builds with value class support are available (as listed on the project page).
To get started:
- Download the Early-Access (EA) build from the Valhalla project page (https://openjdk.org/projects/valhalla)
- Enable preview features when compiling and running:
javac --enable-preview --source 24 Complex.java
java --enable-preview Complex
Top comments (0)