Key Takeaways
Article 9 supports runtime choices by decision criterion, not by language identity. Peak throughput, pooled fixed-rate response time, near-limit p95/p99, resident memory, CPU per 1,000 RPS, and Java GC evidence point to different tradeoffs.
Under Article 9’s documented single-service-host Linux HTTP benchmark setup, peak throughput and near-limit
95pct-go-peakp95/p99 favored the measured Java Helidon SEoracle-jdk-26-jvmandoracle-jdk-26-aotconfigurations.Go
net/httphad the strongest evidence in Article 9’s pooled fixed-offered-rate response-time view and measured resident memory footprint view.Treat
oracle-jdk-26-aotas the benchmark’s measured Oracle JDK configuration with that label. It was not a GraalVM Native Image executable, and it is not a universal Java AOT, Project Leyden, startup, footprint, or steady-state throughput conclusion.
You have the benchmark results now. Article 9 reported results for a compact Go net/http service and a Helidon SE Java service running on two measured Oracle JDK runtime shapes.
So the question becomes the one teams actually ask in architecture reviews:
Which runtime should we choose?
That sounds like a request for one answer. It is not. A runtime decision is a workload decision, and the workload in this series is intentionally narrow: a single-service-host Linux HTTP benchmark with controlled fixtures, fixed-rate comparisons, near-limit comparisons, RSS measurements, CPU normalization, and Java GC pause evidence.
It is not a production microservices simulation. It does not include database execution, vector search latency, model inference, TLS, request logging, Kubernetes, autoscaling, service mesh behavior, or multi-node deployment.
That narrowness is what makes the benchmark useful. We can say concrete things about this service shape without pretending those things automatically apply to every service your team might operate.
This final article translates Article 9’s published results into runtime-selection guidance. It does not add a new benchmark run or introduce a new metric table. It answers the practical question: given the reported results, which runtime shape is attractive for which decision criterion, and where should we stop generalizing?
Start with the decision this benchmark can help with
Article 9 helps when your service resembles the benchmark: compact, HTTP-based, single-service-host, and affected enough by local runtime behavior that the runtime choice matters.
Imagine you are building a small service tier in front of an AI application path. The service validates JSON, does a small amount of deterministic local work, maybe prepares a call to a database or embedding service, and returns a compact response.
You are not trying to benchmark the database yet. You are trying to decide whether the local HTTP service boundary should start as Go net/http or Java Helidon SE on the measured Oracle JDK runtime shapes.
That is the kind of decision Article 9 can inform. It gives you evidence about local HTTP request handling, runtime scheduling, serialization, CPU use, memory footprint, and response-time behavior under the documented benchmark setup.
It does not answer the full production question for a service that spends most of its time waiting on a database, cache, queue, remote API, model endpoint, TLS stack, logging pipeline, tracing agent, or service mesh. Those are real service costs. They just were not part of this benchmark’s primary result.
The right way to read Article 9 is therefore not:
Which language won?
The better question is:
Which measured runtime shape best fits the constraint I care about for this service boundary?
That wording matters. A peak-throughput decision is different from a memory-density decision. A pooled fixed-rate response-time decision is different from a near-limit p95/p99 decision. A Java ecosystem and diagnostics decision is different from a narrow RSS decision.
All runtime recommendations in this article depend on Article 9’s published result tables and runtime manifest. The labels go-http, oracle-jdk-26-jvm, and oracle-jdk-26-aot refer only to those measured configurations under the documented single-service-host Linux HTTP benchmark setup.
Keep the runtime labels attached to the measured shapes
A runtime shape is the measured combination of application implementation, runtime, configuration, operating environment, and benchmark setup.
Article 9 compared three runtime labels:
go-http: the measured Gonet/httpservice shape.oracle-jdk-26-jvm: the measured Helidon SE service configuration on the documented Oracle JDK 26 JVM-shaped runtime.oracle-jdk-26-aot: the measured Helidon SE service configuration on the documented Oracle JDK 26 AOT-labeled runtime configuration.
Use Article 9’s runtime manifest and result tables as the source of truth for the exact JDK build, flags, operating system, CPU shapes, packaging, offered-rate basis, and measured cells behind those labels.
Those labels are shorthand for measured configurations. They are not standalone claims about every Go, Java, Helidon, OpenJDK, or Oracle JDK deployment. The Java labels refer to the Oracle JDK 26 builds and runtime configurations used in Article 9. They should not be read as results for every OpenJDK distribution, every vendor build, or every Java runtime.
That distinction is especially important for oracle-jdk-26-aot. In this article, oracle-jdk-26-aot means only the Article 9 configuration labeled that way in the public runtime manifest. The label does not mean a GraalVM Native Image executable. It refers only to the Oracle JDK runtime configuration documented for this benchmark, and it should not be read as a general Project Leyden result or as a claim about all Java AOT techniques.
Read each Article 9 view as a different decision lens
Each Article 9 result view answers a different runtime-selection question. Keep the recommendation attached to the view it came from.
- Peak throughput
- What Article 9 measured: Which measured runtime shape led each displayed fixture and CPU-shape peak cell
- How to use it: Use this when throughput headroom is the primary constraint
- Pooled fixed-offered-rate response time
- What Article 9 measured: Pooled p50, p95, p99, and mean response time across Article 9’s fixed-rate records
- How to use it: Use this as an aggregate response-time view under the documented load-generation method, not as a per-cell guarantee
- Near-limit p95/p99
- What Article 9 measured: Response time at the
95pct-go-peakoffered-rate basis - How to use it: Use this when high-load tail behavior near the Go-derived comparison point is the main concern
- What Article 9 measured: Response time at the
- RSS and RSS per 1,000 RPS
- What Article 9 measured: Measured service resident memory footprint, both raw and normalized
- How to use it: Use this when memory density is a first-order platform constraint
- CPU per 1,000 RPS
- What Article 9 measured: CPU use normalized by throughput for measured near-limit rows
- How to use it: Use this per matching fixture and CPU-shape cell, not as a runtime-wide ranking
- Java GC pause evidence
- What Article 9 measured: Java-only GC pause data for the measured Java shapes
- How to use it: Use this as Java runtime-burden context, not as a symmetric Go-versus-Java counter comparison
Read all response-time claims under Article 9’s documented load-generation and latency-recording method. Fixed offered rate by itself should not be treated as proof that coordinated omission was eliminated; the interpretation depends on the complete measurement method used in Article 9.
Where this article says “median p95” or “median p99,” it means Article 9’s median across repeated measurements of the p95 or p99 response-time statistic for that cell.
The practical rule is simple: do not collapse these views into one “winner.” Choose the metric that matches the decision you need to make.
Choose by decision criterion, not by language identity
The compact interpretation of Article 9 is this: Java was stronger for peak-throughput and near-limit p95/p99 evidence, while Go was stronger for pooled fixed-rate response time and resident memory evidence.
- Peak-throughput envelope
- Article 9’s result points first to…: Java Helidon SE on the measured Oracle JDK shapes
- Why: In Article 9’s published peak-throughput table, the measured Java shapes led the displayed peak cells:
oracle-jdk-26-jvmled six of nine cells andoracle-jdk-26-aotled three
- Pooled fixed-offered-rate response time
- Article 9’s result points first to…: Go
net/http - Why: Go had the lowest pooled p50, p95, p99, and mean values under Article 9’s documented load-generation method
- Article 9’s result points first to…: Go
- Near-limit p95/p99 at
95pct-go-peak- Article 9’s result points first to…: Java Helidon SE on the measured Oracle JDK shapes
- Why: Both measured Java runtime shapes had lower median p95 and p99 values than Go in the displayed cells
- Resident memory footprint
- Article 9’s result points first to…: Go
net/http - Why: In Article 9’s displayed near-limit rows, Go had lower measured service RSS than both measured Java shapes
- Article 9’s result points first to…: Go
- CPU per 1,000 RPS
- Article 9’s result points first to…: No single runtime-wide default
- Why: CPU per 1,000 RPS varied by fixture and CPU shape
- Java runtime-burden analysis
- Article 9’s result points first to…: Compare the two measured Java shapes
- Why: Java GC pause evidence helps explain Java runtime behavior, but it is not a complete latency explanation
For peak-throughput envelope, Article 9 favored the measured Java runtime shapes. In the published peak-throughput table, oracle-jdk-26-jvm led six of nine displayed fixture and CPU-shape peak cells, and oracle-jdk-26-aot led three. Treat that as a result for the displayed cells, not as a general Java-over-Go claim.
For pooled fixed-offered-rate response time, Article 9 favored Go. In that pooled view, Go had the lowest p50, p95, p99, and mean response-time values. This should not be read as a per-cell guarantee for every fixture and CPU shape.
For near-limit p95 and p99, Article 9 favored the measured Java runtime shapes. At Article 9’s 95pct-go-peak offered-rate basis — an offered rate tied to Go’s measured peak for the matching fixture and CPU-shape cell — both measured Java runtime shapes had lower median p95 and p99 values than Go in the displayed cells.
For resident memory footprint, Article 9 favored Go. In the displayed near-limit rows, Go had lower measured service RSS than both measured Java runtime shapes, both raw and normalized per 1,000 RPS. If memory density is a first-order requirement for a similar compact service, Go has the stronger evidence.
For CPU per 1,000 RPS, Article 9 does not support one runtime-wide ranking. The result varied by fixture and CPU shape. Some Java cells were lower than Go; others were higher. If CPU per unit throughput decides the architecture, read the matching fixture and CPU-shape cell.
For Java GC pause evidence, Article 9 gives useful Java-only context. The evidence varied by Java runtime shape, fixture, and CPU shape. Use it to compare the measured Java runtime shapes, not to explain all response-time behavior or compare symmetrically with Go runtime counters.
That is the core takeaway: the recommendation changes with the metric.
If you need peak-throughput headroom in a service shaped like the benchmark, the measured Java runtime shapes have the stronger evidence. If you need the lowest pooled fixed-rate response-time view or the smallest measured resident memory footprint, Go has the stronger evidence. If you need near-limit p95/p99 behavior at the Go-derived 95pct-go-peak basis, the measured Java runtime shapes have the stronger evidence.
Where Go net/http looked attractive
Go net/http looked strongest in two places that matter to developers and platform engineers: pooled fixed-offered-rate response time and resident memory footprint.
In Article 9’s pooled fixed-offered-rate view, Go had the lowest p50, p95, p99, and mean response-time values. That view aggregates the fixed-rate records and reports response time in milliseconds. Read it as a pooled result under Article 9’s documented load-generation method, not as a guarantee that every individual fixture and CPU-shape cell favored Go.
Go’s memory result was the clearest Go-favoring resource result in Article 9. In the displayed near-limit rows, Go had lower measured service RSS than both measured Java runtime shapes. Article 9 also showed that RSS per 1,000 RPS preserved the same pattern. That matters because memory density is often a first-order platform concern for compact services.
Use the Go result when the service boundary looks like the benchmark: small, direct, and operationally simple. Go net/http is a good early candidate when the team wants a compact runtime surface, the service does not require Java ecosystem continuity, and resident memory density matters.
A practical Go-first rule looks like this:
If the service resembles the benchmark,and pooled fixed-rate response time or resident memory footprint is the deciding criterion,and the team does not need Java ecosystem continuity for this boundary,then start the prototype with Go net/http.Then add the real dependencies and observability path before making it a platform rule.
That last sentence is the difference between using a benchmark and overusing it. A benchmark can point you to the first runtime to test. It cannot remove the need to measure your representative service.
Where Java Helidon SE on the measured Oracle JDK shapes looked attractive
Java Helidon SE on the measured Oracle JDK shapes looked strongest for peak-throughput envelope and near-limit p95/p99 behavior.
In Article 9’s peak-throughput table, the oracle-jdk-26-jvm shape led six of nine fixture and CPU-shape peak cells, and the oracle-jdk-26-aot shape led the other three. Go did not lead a peak-winner cell in that table. For a compact service shaped like this benchmark, that is a direct and useful result: when peak throughput is the deciding metric, the evidence supports Java Helidon SE on the measured Article 9 Oracle JDK runtime shapes.
Near the Go-derived limit, the Java result was also strong. At Article 9’s 95pct-go-peak offered-rate basis, both measured Java runtime shapes had lower median p95 and p99 response-time values than Go in the displayed fixture and CPU-shape cells.
That does not contradict the pooled fixed-rate response-time result. It tells us the response-time decision depends on the offered-rate basis.
This is a common benchmark trap. A team may quote the pooled fixed-rate table to make one response-time argument, while another team quotes the near-limit table to make a high-load p99 argument. Both may be reading real evidence, but they are answering different questions.
So keep the question attached to the metric:
- What had the lowest pooled fixed-offered-rate response-time values?
- Article 9 view to use: Pooled fixed-offered-rate p50, p95, p99, and mean
- Runtime shape favored in that view: Go
net/http
- What had lower p95/p99 near the Go-derived offered-rate limit?
- Article 9 view to use:
95pct-go-peaknear-limit p95/p99 - Runtime shape favored in that view: Measured Java Helidon SE runtime shapes
- Article 9 view to use:
- What had the strongest peak-throughput envelope?
- Article 9 view to use: Peak-throughput table
- Runtime shape favored in that view: Measured Java Helidon SE runtime shapes
Java Helidon SE on the measured Oracle JDK shapes may also be attractive for non-benchmark reasons when operational continuity matters. JVM diagnostics, GC logs, Java Flight Recorder, Java libraries, Helidon SE integration, existing deployment pipelines, and established incident runbooks can influence production selection. Those factors are engineering inputs outside the benchmark result, not additional throughput or response-time evidence.
A practical Java-first rule looks like this:
If the service is part of a Java platform,and either peak-throughput envelope or near-limit p95/p99 behavior is the deciding benchmark criterion,then start with Java Helidon SE on the measured Oracle JDK runtime shape closest to the service goal.If JVM diagnostics, Java libraries, Helidon SE integration, existing deployment pipelines,or incident runbooks dominate the operational decision,treat those as separate production inputs and verify the measured tradeoffsunder your actual service path.
That is a defensible position in an architecture review. It does not pretend Java wins every row. It says exactly where the Article 9 evidence supports Java and where production operations may reasonably keep Java as the preferred runtime for this service boundary.
CPU and GC do not collapse into one runtime ranking
CPU per 1,000 RPS and Java GC pause evidence are useful, but they do not produce a single Go-versus-Java ranking.
Some measured Java near-limit cells used less CPU per 1,000 RPS than Go. Other cells used more. The answer depended on fixture and CPU shape. That means CPU is not a simple tie-breaker for either side. If CPU per unit throughput is your deciding metric, read the matching row from Article 9 rather than quoting one runtime label.
Because the CPU result is mixed across Article 9’s displayed fixture and CPU-shape cells, this article does not recommend a runtime from CPU alone. If CPU cost is the constraint, choose the row that matches your service shape and retest with your real dependencies.
GC evidence needs the same discipline. Article 9’s Java GC pause evidence helps compare the two measured Java runtime shapes. It does not compare symmetrically with Go runtime counters, and it does not explain all response-time behavior by itself. Response time also includes request handling, scheduling, serialization, CPU availability, memory behavior, and the load point.
A compact Java-only reading is enough here:
- The
oracle-jdk-26-jvmshape is the Java baseline for this benchmark. Its GC pause burden varied by fixture and CPU shape. - The
oracle-jdk-26-aotshape had lower GC pause milliseconds per workload second in several near-limit Java cells, but not all. Its p95 and max pause values also varied by cell. - Use GC pause evidence as Java runtime-burden context. Keep it as one part of the latency story, not the whole story.
This is one of the useful things Article 9 added. Without GC evidence, Java tail behavior is easy to explain by assumption. With GC evidence, we can talk about measured Java runtime burden. But response time is still the result of the full request path, not one counter.
What the oracle-jdk-26-aot shape showed
The oracle-jdk-26-aot result is useful, but it is also the easiest result to overstate.
In this article, oracle-jdk-26-aot means the specific measured Article 9 Java runtime configuration with that label. It was not a GraalVM Native Image executable. Read it as a measured runtime shape for this Helidon SE service under this benchmark setup, not as a general Project Leyden benchmark result, a GraalVM Native Image result, or proof that every Java application will start faster, use less memory, or deliver better steady-state throughput.
What did Article 9 show? The AOT-labeled runtime changed the Java profile in some cells. It led three of nine peak-throughput cells. It often had lower near-limit p95 and p99 than the JVM shape, but not always. It sometimes had lower RSS than the JVM shape, but not consistently. Its GC pause burden was lower in several near-limit cells, but not all.
That is useful precisely because it is bounded. The AOT-labeled runtime deserves a place in a Java evaluation for a similar service boundary. It should not be treated as a universal improvement over the JVM shape.
A safe way to use the result is:
If you want Java Helidon SE,and the service resembles the benchmark,and runtime-shape differences inside Java are important,then include both the oracle-jdk-26-jvm and oracle-jdk-26-aot shapes in your local test.Measure startup, warmup, RSS, CPU, GC, and steady-state behavior under your own service path.
If startup or warmup is part of your reason for exploring an AOT-shaped path, measure startup and warmup directly in your environment. Article 9’s public interpretation focused on throughput, response time, RSS, CPU, and Java GC evidence. Keep startup and warmup as separate measurements unless your own test captures them.
What changed from the first article’s benchmark framing
Article 1 set the benchmark contract and the questions. Article 9 supplied the measured results, so the guidance can now move from planned evaluation criteria to evidence-backed runtime-selection criteria.
At the start of the series, the responsible language was about what we planned to test: equivalent work, Go net/http, Helidon SE, Oracle JDK runtime scope, load-generator validation, CPU and memory visibility, warmup, repeats, resource metrics, and analysis discipline.
Article 9 changed the conversation because it gave us measured results. We no longer have to talk only about expectations or fairness principles. We can tie runtime guidance to measured result categories:
- Peak throughput
- Runtime-selection reading: Favored the measured Article 9 Oracle JDK Java runtime shapes
- Pooled fixed-offered-rate response time
- Runtime-selection reading: Favored Go
- Near-limit p95 and p99 at
95pct-go-peak- Runtime-selection reading: Favored the measured Java runtime shapes
- Resident memory
- Runtime-selection reading: Favored Go
- CPU per 1,000 RPS
- Runtime-selection reading: Mixed by fixture and CPU shape
- Java GC pause evidence
- Runtime-selection reading: Helped describe Java runtime burden without proving full response-time causality
oracle-jdk-26-aot- Runtime-selection reading: Changed the Java profile in some cells but was not a universal improvement over the JVM shape
That matters to different readers for different reasons.
Developers can use the result to choose a first implementation candidate for a similar compact service. Platform engineers can separate throughput, memory, CPU, and high-load response-time criteria instead of arguing about one runtime label. Performance reviewers can check that the recommendation follows the metric. Engineering leaders can use the result in standards discussions without turning it into a blanket Go-versus-Java claim.
The next step is practical: pick the criterion your service actually cares about, choose the runtime shape that Article 9 supports for that criterion, and then measure your representative service path.
How far these findings travel
The findings travel best to services that look like the benchmark: compact Linux HTTP services where local request handling, runtime scheduling, serialization, CPU use, memory footprint, and response-time behavior are meaningful parts of the service boundary.
The result supports workload-specific comparisons among the measured Article 9 runtime shapes. It does not support a universal Go-versus-Java conclusion.
It supports runtime-selection guidance for a compact, intentionally simple, single-service-host Linux HTTP benchmark under Article 9’s documented load-generation setup. It does not support claims about production microservices with databases, caches, queues, remote APIs, service meshes, or complex network dependencies unless those paths are separately measured.
It supports comparison of Go net/http with the measured Java Helidon SE runtime shapes under the Article 9 setup. It does not support claims about all Go frameworks, all Java frameworks, all Helidon services, or all JVM distributions.
It supports interpretation of peak throughput, pooled fixed-rate response time, near-limit p95/p99, resident memory, CPU per 1,000 RPS, and Java GC pause evidence as reported in Article 9. It does not support claims about Kubernetes autoscaling, pod density, cloud cost, multi-node scaling, or platform scheduling behavior unless your own test includes those factors.
It supports treating oracle-jdk-26-aot as one measured Oracle JDK AOT-labeled runtime configuration in this benchmark. It does not support treating that configuration as proof of universal Java startup, footprint, steady-state throughput, or Project Leyden behavior.
These boundaries should make the result more useful, not less. A benchmark that carries its boundary is easier to cite responsibly.
A bounded way to summarize the result is:
For a compact Linux HTTP service shaped like Article 9, Java Helidon SE on the measured
oracle-jdk-26-jvmandoracle-jdk-26-aotshapes had the stronger peak-throughput and near-limit p95/p99 evidence, while Gonet/httphad the stronger pooled fixed-rate response-time and resident-memory evidence.
That is different from saying:
Java is faster than Go.
Or:
Go is always better for microservices.
Or:
The AOT-labeled runtime proves Java startup and footprint are solved.
The bounded statement helps a team choose what to test first. The broad statements start a language argument and hide the actual evidence.
Applying the result to a real service path
Use Article 9 as a first filter, then build a representative test for the real service path.
Suppose you are building a retrieval API for an AI application. The service accepts a JSON request, validates tenant policy, prepares a vector-search query, calls a database, formats candidate chunks, and returns a response to another service.
Article 9 helps you reason about the local service-tier portion: HTTP request handling, JSON work, deterministic local work, runtime scheduling, resident memory, CPU, and response-time behavior under the benchmark setup.
It does not tell you the vector-search latency. It does not tell you the SQL plan quality. It does not tell you connection-pool behavior. It does not tell you embedding model latency. It does not tell you how tracing, TLS, retries, or structured logging changes the p99.
So use the benchmark as a first filter:
- If resident memory density and Article 9’s pooled fixed-rate response-time view dominate the service-tier decision, prototype Go
net/httpearly. - If peak-throughput envelope, near-limit p95/p99, JVM diagnostics, Java libraries, Helidon SE fit, or existing Java operations dominate, prototype Java Helidon SE on the measured Article 9 Oracle JDK runtime shapes early.
- If Java is already likely and runtime-shape behavior matters, include both
oracle-jdk-26-jvmandoracle-jdk-26-aotin the local comparison. - If database calls, model calls, TLS, logging, tracing, retries, or failure behavior dominate the path, build a representative test before standardizing.
The goal is not to make the benchmark bigger for the sake of looking realistic. The goal is to make your next measurement representative of the decision you need to make.
Conclusion: Choose the metric, then choose the runtime
Use Article 9 to narrow the runtime choice for services that resemble the benchmark. Use your own measurements to make the production decision.
- Peak throughput for this compact service shape
- Reasonable starting point: Java Helidon SE on the measured Article 9 Oracle JDK runtime shapes
- Article 9’s pooled fixed-offered-rate response-time view
- Reasonable starting point: Go
net/http
- Reasonable starting point: Go
- Measured resident memory footprint
- Reasonable starting point: Go
net/http
- Reasonable starting point: Go
- Near-limit p95/p99 at the
95pct-go-peakbasis- Reasonable starting point: The measured Java runtime shapes
- CPU per 1,000 RPS
- Reasonable starting point: The matching fixture and CPU-shape cell in Article 9
- JVM diagnostics, Java ecosystem fit, Helidon SE integration, and existing operational standards
- Reasonable starting point: Java Helidon SE may remain the more defensible starting point for this service boundary
The final answer is not “Go wins” or “Java wins.” The final answer is more useful:
Choose the measured runtime shape supported by the metric that matters for your service, and keep the benchmark boundary attached.
That is how to use a benchmark as engineering evidence instead of a language contest. Measure the boundary, interpret response time with resource evidence, and choose the runtime your workload and team can defend.
So, can a Java HTTP service shaped like this benchmark be competitive with the measured Go service? For this benchmark-bounded case, yes. In this compact HTTP service benchmark, the Java Helidon SE implementation running on the tested Article 9 Oracle JDK runtime shapes was competitive with the Go net/http implementation under the documented hardware, load-generation method, and reported Article 9 metrics.
That is a useful result for runtime selection. It is not a universal law about every Java service, every Go service, or every production workload.
