Backend Development 9 min read

Performance Comparison of Spring Boot Native Image, JAR, Go, and Rust Deployments

The article benchmarks a Spring Boot 3 service as a GraalVM native image, a traditional JAR, and Go and Rust versions, showing the native binary starts in under a second with ~70 MB memory and 7 k requests/s, the JAR needs seconds and 200 MB, while Go and Rust use 10 MB and 3 MB respectively with throughput, illustrating native images’ fast startup and lower runtime footprint despite longer compilation.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Performance Comparison of Spring Boot Native Image, JAR, Go, and Rust Deployments

This article evaluates the fast startup and memory characteristics of a Spring Boot 3 application built as a GraalVM native binary (AOT‑processed) and compares it with the traditional JAR deployment as well as equivalent services written in Go and Rust.

Binary (native image) deployment

@RestController
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/customers")
    Collection
customers() {
        return Set.of(new Customer(1, "A"), new Customer(2, "B"), new Customer(3, "C"));
    }

    record Customer(Integer id, String name) {}
}

Running the native image on a test machine yields a startup time of less than one second and a memory footprint of about 70 MB under load (≈20 MB idle). The ApacheBench results are:

$ ab -c 50 -n 10000 http://localhost:8080/customers
Server Software:
Server Hostname:        localhost
Server Port:            8080

Document Path:          /customers
Document Length:        61 bytes

Concurrency Level:      50
Time taken for tests:   1.413 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1660000 bytes
HTML transferred:       610000 bytes
Requests per second:    7076.39 [#/sec] (mean)
Time per request:       7.066 [ms] (mean)
Transfer rate:          1147.15 [Kbytes/sec] received
Connection Times (ms)
               min  mean[+/-sd] median   max
Connect:        0    2   8.0      2     144
Processing:     1    5   6.7      4     147
Waiting:        0    4   5.6      3     145
Total:          1    7  10.4      6     149

JAR deployment

The same application packaged as a regular JAR occupies 19 MB. Under the same load the memory usage rises to about 200 MB (≈160 MB idle) and the startup cost is higher because JVM warm‑up is required.

$ ab -c 50 -n 10000 http://localhost:8080/customers
... (same format) ...
Time taken for tests:   17.930 seconds
Requests per second:    557.72 [#/sec] (mean)
Time per request:       89.651 [ms] (mean)

Go implementation (standard library only)

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "net/http"
)

var port = flag.String("p", "8080", "please input port")

func main() {
    http.HandleFunc("/customers", func(w http.ResponseWriter, r *http.Request) {
        data, _ := json.Marshal(r.URL)
        w.Write(data)
    })
    e := make(chan error)
    go func() {
        e <- fmt.Errorf("error[%v]", http.ListenAndServe(":"+*port, nil))
    }()
    fmt.Println("http server started...")
    fmt.Println(<-e)
}

The Go service runs with about 10 MB idle memory (≤20 MB with a framework) and achieves:

$ ab -c 50 -n 10000 http://localhost:8080/customers
... (results) ...
Requests per second:    7247.68 [#/sec] (mean)
Time per request:       6.899 [ms] (mean)

Rust implementation (Actix‑web)

[dependencies]
actix-web = "4"

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
}

Actix‑web consumes roughly 3 MB idle and about 6 MB under load, delivering:

$ ab -c 50 -n 10000 http://localhost:8080/customers
... (results) ...
Requests per second:    9163.48 [#/sec] (mean)
Time per request:       5.456 [ms] (mean)

Conclusion

GraalVM native images provide dramatically faster startup (seconds vs tens of seconds) and lower runtime memory compared with traditional JARs, at the cost of long compilation times (≈15 minutes for the demo). The results show that Java can compete with Go and Rust in cloud‑native scenarios while offering a familiar ecosystem.

JavaPerformancerustGoSpring BootGraalVMnative-image
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.