Performance Comparison of Spring Boot Native Image vs Go and Rust
This article demonstrates how to quickly build and benchmark a Spring Boot 2.x application as a native binary, compares its startup time, memory usage, and request throughput with equivalent implementations in Go and Rust, and concludes that AOT‑processed Java offers impressive performance despite longer compilation times.
Spring Boot 2.x is no longer maintained, prompting a migration to JDK 21 and native image builds. The article shows how to generate a binary using start.spring.io and provides a minimal @RestController example.
@RestController
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@GetMapping("/customers")
Collection<Customer> customers() {
return Set.of(new Customer(1, "A"), new Customer(2, "B"), new Customer(3, "C"));
}
record Customer(Integer id, String name) {}
}The binary starts in seconds, uses about 70 MB memory at peak and 20 MB idle, and achieves roughly 7 076 requests per second in a benchmark.
When packaged as a traditional JAR, the application size is 19 MB, memory usage rises to around 200 MB under load and drops to about 558 requests per second, indicating the need for JVM warm‑up.
For comparison, a Go implementation using only the standard library consumes roughly 10 MB memory and reaches about 7 248 requests per second, while a Rust Actix‑web version uses about 3 MB idle memory and achieves roughly 9 163 requests per second.
package main
import (
"encoding/json"
"flag"
"fmt"
"net/http"
)
var port = flag.String("p", "8080", "please input port")
func main() {
http.HandleFunc("/customers", func(writer http.ResponseWriter, request *http.Request) {
data, _ := json.Marshal(request.URL)
writer.Write(data)
})
e := make(chan error)
go func() {
e <- fmt.Errorf("error[%v]", http.ListenAndServe(":"+*port, nil))
}()
fmt.Println("http 服务器启动...")
fmt.Println(<-e)
} use actix_web::{get, App, HttpRequest, HttpResponse, HttpServer, Responder};
#[get("/customers")]
async fn echo(req: HttpRequest) -> impl Responder {
let url = req.uri().to_string();
HttpResponse::Ok().body(url)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(echo)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}The conclusion is that AOT‑processed Java native images provide fast startup and low memory consumption, solving traditional JVM drawbacks, but the compilation time is significantly longer, taking around 15 minutes on a 2017 MacBook Pro.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.