Key Takeaways
Under this benchmark contract, the Oracle JDK Java runtime shapes led the peak-throughput results. The
oracle-jdk-26-jvmshape led six of nine fixture/CPU-shape peak cells, and theoracle-jdk-26-aotshape led the other three.Response time depended on the comparison point. In the pooled fixed-offered-rate view, Go had the lowest p50, p95, p99, and mean response time. At the
95pct-go-peaknear-limit basis, both Java runtime shapes had lower median p95 and p99 response-time values than Go in every displayed fixture/CPU-shape cell.Memory was the clearest resource tradeoff. Go used much lower measured service RSS in every displayed near-limit row, both as raw resident memory and normalized as RSS per 1,000 RPS.
CPU efficiency did not collapse into one simple ranking. CPU per 1,000 RPS varied by fixture and CPU shape, while Java GC pause evidence helped describe the Java runs without proving full response-time causality.
A service tier in front of Oracle AI Database can sit directly in the hot path. It might validate requests before calling vector search, enrich context before a model call, serve retrieval results to an AI application, or expose a compact HTTP endpoint that has to stay fast under load.
At low volume, runtime choice may not dominate. At higher volume, request scheduling, JSON handling, memory footprint, CPU use, and tail response time become capacity questions.
This is the ninth article in a ten-part, single-machine Linux benchmark series comparing Go and Java service-tier implementations for Oracle AI Database-backed application patterns. The earlier articles established the benchmark contract: equivalent service behavior, Go net/http and Java Helidon SE implementation choices, load generation, run rules, result validation, and analysis discipline.
This article reads the benchmark evidence. It does not rerun the benchmark, change the benchmark contract, or turn the results into runtime-selection advice. That guidance belongs in the next article.
The scope is deliberately narrow. This article compares specific service implementations and runtime shapes under a single-machine Linux HTTP benchmark contract. It does not compare programming languages in the abstract. It also does not measure Oracle AI Database performance, vector search, RAG quality, model inference, TLS, request logging, Kubernetes, autoscaling, cloud deployment, or production fleet behavior.
The tables and charts below use only benchmark runs that passed the validation checks defined earlier in the series.
Runtime Variants Compared
The benchmark result files use compact runtime identifiers. The article uses shorter display labels in tables and charts, but the mapping is always the same:
| Benchmark runtime identifier | Display label used here | What it means in this article |
|---|---|---|
go-http | Go | The Go service implementation using Go net/http |
oracle-jdk-26-jvm | Oracle JDK JVM shape | The Helidon SE Java service running on the benchmark JVM shape |
oracle-jdk-26-aot | Oracle JDK AOT-shaped runtime | The Helidon SE Java service running as the benchmark’s oracle-jdk-26-aot runtime shape |
The Go rows represent the benchmark’s net/http implementation and runtime configuration.
The Java implementation in this benchmark used Helidon SE as configured by the benchmark contract. These results apply to that service implementation and runtime configuration, not to all Helidon services or all Java web frameworks.
The Java rows use the specific Oracle JDK build and runtime artifacts named by the benchmark results. They should not be generalized to every OpenJDK distribution or to untested JVM flags.
The oracle-jdk-26-aot label is deliberately narrow. In this article, oracle-jdk-26-aot means the benchmark-defined Oracle JDK AOT-shaped variant only. It is not a general Project Leyden benchmark result, not a GraalVM Native Image benchmark result, and not evidence for all Java AOT approaches. Do not infer an AOT implementation mechanism, startup behavior, or production deployment model from the label unless it is stated in the benchmark contract.
The CPU shape column uses the benchmark labels 1, 4, and all. Read those as part of the run contract for each result cell, not as a general statement about every host shape or deployment environment. The all label means the benchmark’s “all available CPUs for this run contract” shape; it should not be converted into a portable core count without the benchmark machine details.
How to read the benchmark comparisons
There are three comparison styles in this article.
A peak result reports the strongest throughput result for a benchmark cell. A cell combines the fixture, payload size, CPU shape, runtime shape, process topology, concurrency, and measurement basis. Treat each row as a cell-scoped result, not as a universal statement about Go or Java.
A comparable-rate result compares response time at a common offered-rate basis. This matters because response time from one runtime near saturation should not be casually compared with response time from another runtime at a lighter load point.
A near-limit result in this article means the 95pct-go-peak rate tier. For each fixture and CPU shape, the offered request rate is derived from Go’s measured peak for that same fixture and CPU shape. That lets us ask a practical question: if the service tier is operating near Go’s measured ceiling for this workload cell, how do the other runtime shapes behave at the same offered rate?
The article reports externally observed request durations at named offered-rate tiers. It does not make a coordinated-omission-correction claim; response-time results should be read as benchmark-output measurements under the documented load-generation and analysis contract.
The ratio tables use Go as the denominator for the same fixture and CPU shape:
ratio versus Go = measured value for comparison runtime / measured Go value
Metric direction matters:
- Throughput: higher is better.
- Response time: lower is better.
- RSS: lower is better.
- CPU per 1k RPS: lower is better.
- RSS per 1k RPS: lower is better.
- GC pause metrics: lower is generally better, but they are Java-only context.
Two normalized resource metrics appear often:
CPU per 1k RPS = service CPU percent / (achieved RPS / 1000)RSS per 1k RPS = service RSS MiB / (achieved RPS / 1000)
The CPU values use the benchmark’s recorded service-process CPU percentage basis. Values can exceed 100 when the service consumes more than one core-equivalent of CPU during the measurement window.
Normalized metrics are useful for capacity reasoning, but they do not remove the benchmark context. Read them alongside the absolute values.
Pooled fixed-offered-rate response time
Before peak and near-limit results, the pooled fixed-offered-rate view gives useful context.
This is not a peak-throughput table. It is not the same aggregation basis as the later near-limit median tables. It answers a narrower question: across the fixed-offered-rate records in this view, what externally observed response-time distribution did each runtime shape produce?
Because this table is pooled across fixed-offered-rate records in the view, it is useful for broad context. It is not a substitute for the cell-scoped peak and near-limit tables later in the article.
| Runtime shape | p50 response time (ms) | p95 response time (ms) | p99 response time (ms) | Mean response time (ms) | Max response time (ms) | Measured requests |
|---|---|---|---|---|---|---|
go-http | 0.266 | 0.478 | 0.704 | 0.298 | 53.974 | 2,268,000 |
oracle-jdk-26-aot | 0.282 | 0.585 | 0.888 | 0.327 | 22.276 | 2,268,000 |
oracle-jdk-26-jvm | 0.280 | 0.590 | 0.912 | 0.328 | 30.302 | 2,268,000 |
Note the unit change between sections: this pooled fixed-rate table reports response time in milliseconds, while the near-limit table later reports response time in microseconds.
In this pooled fixed-offered-rate view, Go had the lowest p50, p95, p99, and mean response time. The two Java runtime shapes were close to each other. The AOT-shaped runtime was slightly lower at p95, p99, mean, and max; the JVM shape was slightly lower at p50.
That does not contradict the near-limit result later in the article. The pooled table summarizes records from a fixed-offered-rate matrix. The near-limit table summarizes median benchmark-output metrics at the 95pct-go-peak offered-rate tier.
The practical reading: fixed-rate response time is valid only when the offered-rate basis is named. It is useful context, but it should not be used as a substitute for peak or near-limit results.
Peak throughput by benchmark cell
Peak throughput is the first result group most readers ask for.
In the peak-throughput summary table, the Oracle JDK JVM shape led six of nine fixture/CPU-shape cells, and the Oracle JDK AOT-shaped runtime led the other three. Go did not lead a peak-throughput cell in this table.
That is a meaningful benchmark result, but it is still cell-scoped. It describes the service implementations, fixture sizes, CPU shapes, concurrency choices, and single-machine measurement contract. It is not a language-wide ranking.
| Fixture | CPU shape | Peak runtime shape | Achieved RPS | Concurrency | Service RSS MiB | Service CPU % |
|---|---|---|---|---|---|---|
work-128b | 1 | Oracle JDK JVM shape | 29701.20 | 64 | 137.71 | 105.11 |
work-128b | 4 | Oracle JDK JVM shape | 63826.00 | 256 | 588.10 | 244.71 |
work-128b | all | Oracle JDK JVM shape | 60011.60 | 256 | 608.05 | 298.49 |
work-2kb | 1 | Oracle JDK JVM shape | 24696.93 | 64 | 130.95 | 110.13 |
work-2kb | 4 | Oracle JDK JVM shape | 59083.20 | 256 | 619.54 | 256.34 |
work-2kb | all | Oracle JDK AOT-shaped runtime | 62772.07 | 256 | 617.85 | 322.43 |
work-32kb | 1 | Oracle JDK AOT-shaped runtime | 11289.87 | 16 | 123.32 | 104.31 |
work-32kb | 4 | Oracle JDK JVM shape | 41654.40 | 256 | 416.20 | 319.63 |
work-32kb | all | Oracle JDK AOT-shaped runtime | 52697.73 | 256 | 468.93 | 458.27 |
This table shows only the winning runtime shape for each peak cell. The ratio chart that follows compares the peak values against the matching Go baseline for the same fixture and CPU shape.
The strongest peak-throughput value in the table was 63826.00 RPS for the Oracle JDK JVM shape on work-128b, CPU shape 4.
The strongest AOT-shaped peak-winning cell was 62772.07 RPS on work-2kb, CPU shape all.

Figure 1: Peak throughput by runtime, fixture, and CPU shape. Higher is better.

Figure 2: Throughput ratio versus the Go baseline. Higher is better for throughput; each ratio is scoped to matching benchmark cell dimensions.

Figure 3: Service CPU at each runtime’s peak-throughput cell. Lower is better only when compared at similar throughput; read this beside Figure 1.

Figure 4: Resident memory at each runtime’s peak-throughput cell. Lower is better.

Figure 5: Peak throughput versus RSS footprint. Higher and farther left is better.
The charts make the throughput pattern easier to scan. The Oracle JDK Java runtime shapes were strong in the peak-throughput envelope. But peak RPS alone is not the full operating story. A service that delivers more peak throughput can still use more memory, have a different CPU profile, or behave differently at common request rates.
The practical reading: under this benchmark contract, the peak-throughput winners were the two Oracle JDK Java runtime shapes. Carry that forward as workload-specific service-tier evidence, not as a universal claim about all Go or Java services.
Throughput per configured CPU core
Peak RPS answers, “How much traffic did the cell deliver?”
Throughput per configured CPU core asks, “How much traffic did the cell deliver per CPU shape used by the benchmark cell?”
That distinction matters in platform planning. If an application service tier has a fixed CPU budget, peak RPS alone can hide whether a runtime shape used that budget efficiently.

Figure 6: Peak throughput per configured CPU core. Higher is better; this normalized view should be read alongside absolute throughput values.
The per-core chart keeps the main peak story visible while adding a capacity lens. Some cells that look close in raw RPS separate more clearly after CPU normalization; other cells remain close.
The important discipline is not to turn throughput per core into a universal efficiency law. It is a normalized view for these benchmark cells on this benchmark machine.
The practical reading: throughput per core supports capacity analysis inside the benchmark contract. It should be read beside absolute RPS, response time, CPU per 1k RPS, and memory.
Response time at comparable request rates
Response-time comparisons are easy to misuse. If one runtime is near saturation and another is running far below its own peak, their p95 or p99 values are not telling the same story unless the rate basis is named.
The benchmark evidence includes a moderate 80%-of-Go-peak view and a near-limit 95%-of-Go-peak view. Both are Go-derived common offered-rate bases. They compare runtime shapes at request rates tied to Go’s measured peak for the same fixture and CPU shape.

Figure 7: P95 response time at the fixed 5,000 RPS comparison point. Lower is better.
The fixed 5k RPS chart is the lowest common offered-rate view. It is useful because it compares all three runtime shapes at the same modest request rate before the higher-rate Go-derived tiers.

Figure 8: P95 response time at the 80%-of-Go-peak comparison point. Lower is better.
The 80%-of-Go-peak view is the more moderate common-rate chart. It is useful when asking what happens below the near-limit tier.
The near-limit view is more demanding. It asks what happens close to Go’s measured operating ceiling for the same fixture and CPU shape.
The normalized CPU and RSS columns use achieved RPS from the benchmark result output. The table displays the offered RPS tier for readability; achieved RPS is the denominator used in the normalized metrics.
The table below reports the median p50, p95, and p99 response-time values at 95pct-go-peak, together with RSS and normalized resource metrics.
| Fixture | CPU shape | Offered RPS | Runtime shape | p50 (µs) | p95 (µs) | p99 (µs) | RSS MiB | CPU per 1k RPS | RSS MiB per 1k RPS |
|---|---|---|---|---|---|---|---|---|---|
work-128b | 1 | 13300 | Go | 14458.50 | 31988.00 | 41066.50 | 25.33 | 6.80 | 1.90 |
work-128b | 1 | 13300 | Oracle JDK AOT-shaped runtime | 253.00 | 2670.00 | 8690.00 | 167.45 | 7.23 | 12.59 |
work-128b | 1 | 13300 | Oracle JDK JVM shape | 425.00 | 12164.00 | 21468.50 | 190.93 | 8.07 | 14.36 |
work-128b | 4 | 39300 | Go | 3311.50 | 14719.50 | 22601.00 | 28.18 | 6.83 | 0.72 |
work-128b | 4 | 39300 | Oracle JDK AOT-shaped runtime | 515.00 | 5556.00 | 10490.00 | 703.98 | 5.84 | 17.91 |
work-128b | 4 | 39300 | Oracle JDK JVM shape | 865.50 | 6900.00 | 12394.00 | 623.88 | 5.94 | 15.87 |
work-128b | all | 43000 | Go | 2131.00 | 17640.00 | 30183.00 | 32.31 | 8.37 | 0.75 |
work-128b | all | 43000 | Oracle JDK AOT-shaped runtime | 487.50 | 4228.00 | 8192.00 | 528.12 | 7.08 | 12.28 |
work-128b | all | 43000 | Oracle JDK JVM shape | 615.00 | 5381.00 | 9500.50 | 519.51 | 7.31 | 12.08 |
work-2kb | 1 | 11900 | Go | 3561.00 | 19691.00 | 31845.50 | 25.60 | 6.89 | 2.15 |
work-2kb | 1 | 11900 | Oracle JDK AOT-shaped runtime | 234.50 | 1649.00 | 6662.00 | 177.25 | 8.64 | 14.89 |
work-2kb | 1 | 11900 | Oracle JDK JVM shape | 225.00 | 1349.00 | 7511.50 | 177.60 | 8.01 | 14.92 |
work-2kb | 4 | 37400 | Go | 1931.50 | 12966.50 | 20836.00 | 28.43 | 7.13 | 0.76 |
work-2kb | 4 | 37400 | Oracle JDK AOT-shaped runtime | 362.50 | 3248.50 | 7243.50 | 725.62 | 6.46 | 19.40 |
work-2kb | 4 | 37400 | Oracle JDK JVM shape | 462.50 | 4999.50 | 10068.00 | 484.54 | 6.11 | 12.96 |
work-2kb | all | 38700 | Go | 738.50 | 8753.00 | 16220.50 | 31.77 | 8.86 | 0.82 |
work-2kb | all | 38700 | Oracle JDK AOT-shaped runtime | 540.50 | 5377.50 | 10787.50 | 558.51 | 7.82 | 14.43 |
work-2kb | all | 38700 | Oracle JDK JVM shape | 459.00 | 4135.50 | 7914.00 | 427.24 | 7.83 | 11.04 |
work-32kb | 1 | 7000 | Go | 701.50 | 40501.50 | 54907.00 | 25.22 | 11.54 | 3.60 |
work-32kb | 1 | 7000 | Oracle JDK AOT-shaped runtime | 286.00 | 5307.50 | 18049.00 | 167.81 | 13.80 | 23.97 |
work-32kb | 1 | 7000 | Oracle JDK JVM shape | 295.00 | 13849.50 | 35251.00 | 180.66 | 16.17 | 25.81 |
work-32kb | 4 | 25300 | Go | 3223.50 | 18709.00 | 30260.50 | 28.30 | 12.06 | 1.12 |
work-32kb | 4 | 25300 | Oracle JDK AOT-shaped runtime | 383.00 | 4620.00 | 10277.50 | 349.23 | 10.63 | 13.80 |
work-32kb | 4 | 25300 | Oracle JDK JVM shape | 514.00 | 7137.50 | 13662.50 | 386.17 | 10.78 | 15.26 |
work-32kb | all | 33200 | Go | 6481.00 | 36525.50 | 54315.00 | 32.79 | 9.17 | 0.99 |
work-32kb | all | 33200 | Oracle JDK AOT-shaped runtime | 2734.00 | 13070.00 | 20251.00 | 489.96 | 10.35 | 14.76 |
work-32kb | all | 33200 | Oracle JDK JVM shape | 3903.50 | 15295.50 | 22368.00 | 586.55 | 9.82 | 17.67 |
At the 95pct-go-peak offered-rate targets, both Java runtime shapes showed lower median p95 and p99 response-time values than Go in every displayed fixture/CPU-shape cell.
On work-128b, CPU shape 1, Go p95 was 31988.00 µs, the AOT-shaped runtime p95 was 2670.00 µs, and the JVM shape p95 was 12164.00 µs.
On work-2kb, CPU shape all, the p95 gap was smaller: Go was 8753.00 µs, the AOT-shaped runtime was 5377.50 µs, and the JVM shape was 4135.50 µs.

Figure 9: Near-limit p95 response time by benchmark cell. Lower is better.

Figure 10: Near-limit p99 response time by benchmark cell. Lower is better.

Figure 11: P95 response time across comparable offered-rate tiers. Lower is better.
On work-32kb, CPU shape all, Go p99 was 54315.00 µs, the AOT-shaped runtime was 20251.00 µs, and the JVM shape was 22368.00 µs. That difference sits beside a large RSS difference in the opposite direction.
The practical reading: response time is load-point-sensitive. In the pooled fixed-offered-rate view, Go had the lowest aggregate p50, p95, p99, and mean response time. At the 95pct-go-peak near-limit basis, both Java runtime shapes had lower median p95 and p99 response-time values than Go in every displayed fixture/CPU-shape cell.
CPU per 1k RPS is workload-specific
CPU per 1k RPS asks how much measured service CPU was associated with each thousand requests per second. Lower is better within this benchmark scope.
This metric avoids a common trap: a runtime can lead on peak throughput but still consume more CPU per unit of delivered throughput in some cells. Or it can use less CPU per unit throughput in one payload shape and more in another.

Figure 12: Near-limit CPU used per one thousand requests per second. Lower is better.
The CPU story is not a one-direction runtime ranking.
In several near-limit cells, the Java runtime shapes used less CPU per 1k RPS than Go. On work-128b, CPU shape 4, Go used 6.83, the AOT-shaped runtime used 5.84, and the JVM shape used 5.94. On work-2kb, CPU shape 4, Go used 7.13, the AOT-shaped runtime used 6.46, and the JVM shape used 6.11.
In other cells, Go used less CPU per 1k RPS. On work-32kb, CPU shape 1, Go used 11.54, the AOT-shaped runtime used 13.80, and the JVM shape used 16.17. On work-2kb, CPU shape 1, Go used 6.89, the AOT-shaped runtime used 8.64, and the JVM shape used 8.01.
For platform teams, the useful point is that CPU cost depends on fixture and CPU shape. Read the cell; do not read only the runtime label.
The practical reading: CPU per 1k RPS is mixed. Some Java cells had lower normalized service CPU than Go; others had higher normalized service CPU. This is workload-specific capacity evidence, not a universal CPU-efficiency score.

Figure 13: Near-limit CPU per 1k RPS ratio versus the Go baseline. Lower is better.
RSS and RSS per 1k RPS show the clearest resource tradeoff
Memory is the cleanest contrast in the near-limit rows.
Go’s measured service RSS stayed in a narrow band from 25.22 MiB to 32.79 MiB across the displayed near-limit cells. The AOT-shaped Java runtime ranged from 167.45 MiB to 725.62 MiB. The JVM-shaped Java runtime ranged from 177.60 MiB to 623.88 MiB.
That is not a footnote. It is one of the main results.

Figure 14: Near-limit resident set size by benchmark cell. Lower is better for footprint.
RSS means resident memory under the benchmark conditions. It is not a Kubernetes memory request, a production heap-sizing rule, or a container limit recommendation. It depends on service implementation, runtime configuration, operating-system accounting, process topology, and collection method.
Even with that boundary, the pattern is large enough to matter. In this evidence set, Go had much lower measured service RSS than both Java runtime shapes in every displayed near-limit fixture/CPU-shape cell.
RSS per 1k RPS normalizes that footprint by delivered throughput.

Figure 15: Near-limit resident set size normalized per one thousand requests per second. Lower is better.

Figure 16: RSS per 1k RPS across comparable offered-rate tiers. Lower is better.

Figure 17: Near-limit memory cost versus p95 response time. Lower left is better.
The normalized view preserves the same memory story.
On work-128b, CPU shape 4, Go used 0.72 MiB per 1k RPS, the AOT-shaped runtime used 17.91, and the JVM shape used 15.87.
On work-32kb, CPU shape all, Go used 0.99, the AOT-shaped runtime used 14.76, and the JVM shape used 17.67.
A runtime shape can be attractive for peak throughput and high-load response time while still costing more resident memory. That is exactly the kind of tradeoff this benchmark was designed to expose.
The practical reading: memory strongly favored Go in the near-limit evidence. The pattern held for both absolute RSS and RSS per 1k RPS across every displayed near-limit row.
Near-limit ratios versus the Go baseline
The near-limit table gives raw values. Ratios compress the comparison against the Go denominator for the same fixture and CPU shape.
Ratios are useful only when direction is explicit:
- A p95 ratio below
1.0means lower p95 response time than Go for that cell. - An RSS ratio above
1.0means higher resident memory than Go for that cell. - A CPU-per-1k ratio below
1.0means lower CPU per unit throughput than Go for that cell.
In the near-limit ratio rows, AOT-shaped p95 ratios versus Go ranged from 0.08 to 0.61, while JVM-shaped p95 ratios versus Go ranged from 0.07 to 0.47.
For the displayed near-limit cells, Java RSS ratios versus the matching Go row ranged from 6.61x to 25.53x. That ratio describes resident memory only; it does not include heap limits, container requests, or production memory policy.
CPU-per-1k ratios varied by cell, with some Java rows below 1.0 and others above 1.0.

Figure 18: Near-limit p95 response-time ratio versus the Go baseline. Lower is better for response time.

Figure 19: Near-limit RSS ratio versus the Go baseline. Lower is better for memory footprint.
This chart is useful precisely because it is compact. It is also easy to overread. A ratio shows relative magnitude inside a cell. It does not explain the cause, and it does not predict a different service, host, payload mix, database access pattern, or runtime configuration.
The practical reading: the near-limit ratio view makes two patterns obvious. Java p95 and p99 ratios were below Go at the Go-derived near-limit basis, while Java RSS ratios were much higher than Go. CPU ratios varied by cell.
Java GC pause evidence for the Java runs
The GC pause evidence is Java-only evidence. It comes from Java GC pause measurements collected for Oracle JDK workload windows. It is useful runtime-burden context.
It is not a symmetric comparison with Go runtime counters, and it should not be used alone to explain every response-time pattern. In the GC table, “measured events” refers to the event denominator used by the Java GC analysis output, not to GC events unless explicitly stated by the benchmark artifacts.
| Fixture | CPU shape | Runtime shape | Offered RPS | GC pause ms per workload second | GC pause ms per 1k measured events | GC pause p95 ms | GC pause max ms |
|---|---|---|---|---|---|---|---|
work-128b | 1 | AOT-shaped runtime | 13300 | 13.96 | 1.40 | 2.59 | 12.35 |
work-128b | 1 | JVM shape | 13300 | 13.14 | 1.32 | 2.61 | 7.97 |
work-128b | 4 | AOT-shaped runtime | 39300 | 8.85 | 0.30 | 8.14 | 9.05 |
work-128b | 4 | JVM shape | 39300 | 9.08 | 0.31 | 5.69 | 7.20 |
work-128b | all | AOT-shaped runtime | 43000 | 11.43 | 0.35 | 8.63 | 10.33 |
work-128b | all | JVM shape | 43000 | 12.45 | 0.39 | 10.82 | 11.95 |
work-2kb | 1 | AOT-shaped runtime | 11900 | 15.53 | 1.74 | 2.89 | 8.06 |
work-2kb | 1 | JVM shape | 11900 | 15.56 | 1.74 | 3.12 | 5.15 |
work-2kb | 4 | AOT-shaped runtime | 37400 | 7.89 | 0.28 | 7.36 | 7.40 |
work-2kb | 4 | JVM shape | 37400 | 8.50 | 0.30 | 6.80 | 9.42 |
work-2kb | all | AOT-shaped runtime | 38700 | 10.84 | 0.37 | 11.44 | 12.47 |
work-2kb | all | JVM shape | 38700 | 10.11 | 0.35 | 6.96 | 10.91 |
work-32kb | 1 | AOT-shaped runtime | 7000 | 9.31 | 1.77 | 2.96 | 12.50 |
work-32kb | 1 | JVM shape | 7000 | 8.76 | 1.67 | 2.73 | 6.73 |
work-32kb | 4 | AOT-shaped runtime | 25300 | 6.15 | 0.32 | 4.40 | 5.94 |
work-32kb | 4 | JVM shape | 25300 | 8.15 | 0.43 | 6.48 | 20.75 |
work-32kb | all | AOT-shaped runtime | 33200 | 9.92 | 0.40 | 7.71 | 13.91 |
work-32kb | all | JVM shape | 33200 | 10.39 | 0.42 | 8.09 | 9.03 |
The Java GC story is mixed.
The AOT-shaped runtime was numerically lower than the JVM shape for GC pause milliseconds per workload second in several near-limit cells: work-128b CPU shape 4, work-128b CPU shape all, work-2kb CPU shape 1, work-2kb CPU shape 4, work-32kb CPU shape 4, and work-32kb CPU shape all.
The JVM shape was lower in other cells: work-128b CPU shape 1, work-2kb CPU shape all, and work-32kb CPU shape 1.
The p95 and max pause columns vary too. On work-32kb, CPU shape 4, the JVM shape had a max GC pause of 20.75 ms, while the AOT-shaped runtime had 5.94 ms. On work-128b, CPU shape 1, the AOT-shaped runtime had a higher max pause, 12.35 ms, compared with the JVM shape at 7.97 ms.

Figure 20: Java GC pause milliseconds per workload second at the near-limit comparison point. Lower is generally better; this chart provides runtime-burden context for the Java runs.

Figure 21: Java GC pause burden across comparable offered-rate tiers. Lower is generally better.
The GC chart helps avoid hand-waving. Without GC logs, Java tail behavior is often explained by assumption. With GC logs, we can at least say what pause burden was visible in the Java evidence.
But response time also includes request handling, scheduling, serialization, CPU availability, memory behavior, and load point.
The practical reading: Java GC pause evidence was visible and varied by runtime shape, fixture, and CPU shape. It helps interpret the Java runs, but it does not replace the response-time tables or prove full latency causality.
The oracle-jdk-26-aot runtime shape changed the profile, but not uniformly
The oracle-jdk-26-aot result should stay tied to the measured runtime shape.
This article does not claim startup behavior, does not benchmark all Java AOT approaches, and does not describe this as a GraalVM Native Image result.
In the benchmark evidence, the AOT-shaped runtime changed the measured Java profile across some throughput, response-time, RSS, CPU, and GC-pause observations. It did not produce a blanket improvement over the JVM shape across every fixture, CPU shape, and metric.
At peak throughput, the AOT-shaped runtime led three of nine fixture/CPU-shape cells:
| Fixture | CPU shape | AOT-shaped peak RPS | Peak-throughput result |
|---|---|---|---|
work-2kb | all | 62772.07 | Peak leader |
work-32kb | 1 | 11289.87 | Peak leader |
work-32kb | all | 52697.73 | Peak leader |
The JVM shape led the other six peak cells. That means the AOT-shaped runtime was not a universal peak-throughput improvement over the JVM shape in this benchmark.
Near the Go-derived limit, the AOT-shaped runtime often had lower p95 and p99 response time than the JVM shape, but not always.
On work-128b, CPU shape 1, AOT-shaped p95 was 2670.00 µs while JVM-shaped p95 was 12164.00 µs. On work-32kb, CPU shape 4, AOT-shaped p95 was 4620.00 µs while JVM-shaped p95 was 7137.50 µs.
But on work-2kb, CPU shape all, the JVM shape had lower p95 and p99: JVM-shaped p95 was 4135.50 µs compared with AOT-shaped p95 at 5377.50 µs, and JVM-shaped p99 was 7914.00 µs compared with AOT-shaped p99 at 10787.50 µs.
RSS also did not produce a simple “AOT is smaller” result. On work-32kb, CPU shape all, AOT-shaped RSS was 489.96 MiB and JVM-shaped RSS was 586.55 MiB. On work-2kb, CPU shape 4, AOT-shaped RSS was 725.62 MiB and JVM-shaped RSS was 484.54 MiB.
The defensible statement is narrow and still useful: the AOT-shaped runtime changed the Java benchmark profile in some cells. It was not a universal improvement across all measured dimensions.
The practical reading: treat oracle-jdk-26-aot as a measured runtime-shape result. In this evidence, it helped some cells and metrics, but did not uniformly improve peak throughput, response time, memory footprint, CPU normalization, or GC pause burden versus the JVM shape.
Boundaries for using these Go and Java benchmark results
A benchmark result is useful only when its boundaries travel with it.
| Boundary | What this article supports | What it does not support |
|---|---|---|
| Runtime scope | go-http, oracle-jdk-26-jvm, and oracle-jdk-26-aot as benchmark runtime variants | All Go services, all Java services, all Helidon services, or all JDK builds |
| Java distribution scope | The specific Oracle JDK build and runtime artifacts named by the benchmark results | Temurin, Eclipse Adoptium, other OpenJDK distributions, or untested JVM flags |
| AOT scope | The benchmark’s Oracle JDK AOT-shaped runtime cell | GraalVM Native Image, all Project Leyden behavior, or all Java AOT approaches |
| Workload scope | Single-machine Linux HTTP service workload with fixture and CPU shapes | Kubernetes, autoscaling, service mesh, multi-node scaling, production fleet behavior |
| Database scope | Service-tier context relevant to Oracle AI Database-backed AI applications | Oracle AI Database throughput, vector search, SQL performance, RAG quality, model inference, or storage behavior |
| Response-time scope | Externally observed request duration at named rate bases | Production SLO guarantees, coordinated-omission-correction claims, or latency rankings without matching load points |
| RSS scope | Measured resident memory under benchmark conditions | Production heap sizing, Kubernetes memory requests, or container limit recommendations |
| CPU scope | Service-process CPU normalized by delivered throughput | Universal runtime CPU efficiency |
| GC scope | Java GC pause evidence for Java cells | Complete response-time causality or a symmetric comparison with Go runtime counters |
The benchmark is intentionally narrow so the comparisons can be meaningful. If you change the service logic, payload mix, database dependency, network path, TLS behavior, logging, container limits, JDK build, Go version, Helidon configuration, or host shape, measure again.
What the data supports
The data supports a tradeoff story, not a language-war slogan.
Peak throughput favored the Oracle JDK Java runtime shapes in the peak-throughput summary. The JVM shape led six of nine peak cells, and the AOT-shaped runtime led three. The strongest peak-throughput value was 63826.00 RPS for the JVM shape on work-128b, CPU shape 4.
Throughput per core reinforces that raw peak RPS is not the whole capacity story. It shows delivered work per configured core, which is often closer to the resource question platform teams ask.
Response time depends on rate basis. In the pooled fixed-offered-rate table, Go had the lowest p50, p95, p99, and mean response time. At the 95pct-go-peak near-limit basis, both Java runtime shapes had lower median p95 and p99 response-time values than Go in every displayed fixture/CPU-shape cell.
Memory strongly favored Go. In every displayed near-limit row, Go had much lower measured service RSS than both Java runtime shapes. RSS per 1k RPS preserved that memory-cost pattern.
CPU per 1k RPS was mixed. Some Java cells used less CPU per unit of delivered throughput than Go; others used more. The answer depends on fixture and CPU shape.
Ratios versus Go are useful scoped summaries. They make the near-limit p95 and RSS contrast obvious, but they must be read with the raw tables because metric direction changes.
Java GC pause evidence helps interpret the Oracle JDK runs, but it does not replace the response-time charts. GC pause burden varied by Java runtime shape, fixture, and CPU shape.
The AOT-shaped runtime changed the measured Java profile in some cells. It was not a universal improvement over the JVM shape, and it should not be read as a broad product or technology promise beyond the measured workload.
References and methodology notes
The numeric tables and charts in this article come from the benchmark results for this series. The tables preserve the result dimensions used by the analysis: fixture, CPU shape, runtime shape, offered-rate basis, and measured service metrics. The external references below provide product, API, and methodology context; they do not expand the measured benchmark scope.
- Go
net/httppackage documentation - Go runtime package documentation
- Helidon documentation
- Helidon SE introduction
- Oracle Java SE documentation
- Oracle Java downloads
- OpenJDK JDK 26 project — background on the JDK release line; the benchmark results are limited to the Oracle JDK variants.
- Project Leyden — background only; this article does not report a broad Project Leyden benchmark result.
- HdrHistogram
Next: turning benchmark results into runtime choices
This article gives us enough evidence to say something concrete.
Under this benchmark’s single-machine Linux HTTP service workload, the Oracle JDK JVM and AOT-shaped runtime delivered the highest peak-throughput results in the recorded peak cells. Near the Go-derived high-load basis, the Java service shapes had lower median p95 and p99 externally observed response-time values in the near-limit output. Go used far less resident memory, both raw and normalized per 1,000 RPS. CPU efficiency depended on fixture and CPU shape. Java GC pause measurements showed real differences between JVM-shaped and AOT-shaped Java cells, but not a universal AOT advantage.
That is useful because it is specific.
The next article can translate these results into runtime-selection guidance: when peak throughput matters more than RSS, when memory density matters more than high-load headroom, when fixed-rate response time is the better comparison, and what to measure in an Oracle AI Database-backed AI service before choosing a runtime shape.



































You must be logged in to post a comment.