---
title: "Rust vs Java: Performance e Segurança | Rust Brasil"
url: "https://rustlang.com.br/artigos/rust-vs-java/"
markdown_url: "https://rustlang.com.br/artigos/rust-vs-java.MD"
description: "Rust vs Java: GC vs ownership, performance, concorrência, ecossistema e quando migrar. Comparação completa 2026."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Rust vs Java: Performance e Segurança | Rust Brasil

Rust vs Java: GC vs ownership, performance, concorrência, ecossistema e quando migrar. Comparação completa 2026.


## Introdução

Java e Rust representam duas filosofias distintas para construir software confiável em larga escala. **Java**, com quase 30 anos de história, é a espinha dorsal de sistemas empresariais, bancos, telecoms e aplicações Android. Sua JVM (Java Virtual Machine) oferece portabilidade, um Garbage Collector maduro e um ecossistema massivo. **Rust** compila diretamente para código nativo, eliminando o GC em favor de ownership e borrowing, resultando em binários menores, startup instantâneo e consumo de memória previsível.

Este artigo é para desenvolvedores Java que estão avaliando Rust, arquitetos decidindo a stack de novos microsserviços, e qualquer pessoa curiosa sobre como essas linguagens se comparam na prática.

## Tabela Comparativa

| Aspecto | Rust | Java |
|---|---|---|
| **Execução** | Código nativo (compilado, LLVM) | Bytecode JVM (JIT compilado) |
| **Gerenciamento de memória** | Ownership + Borrow Checker | Garbage Collector (G1, ZGC, Shenandoah) |
| **Tempo de startup** | < 1 ms | 100-500 ms (JVM warmup: segundos) |
| **Consumo de memória** | 5-20 MB (servidor típico) | 100-500 MB (JVM heap) |
| **Concorrência** | async/await + threads + channels | Virtual Threads (Loom), ExecutorService |
| **Null safety** | Option\<T\> (sem null) | Optional + anotações (null existe) |
| **Generics** | Monomorphization (zero-cost) | Type erasure (overhead em runtime) |
| **Ecossistema** | crates.io (~150k) | Maven Central (~600k artifacts) |
| **Frameworks web** | Axum, Actix Web | Spring Boot, Quarkus, Micronaut |
| **Curva de aprendizado** | Íngreme (ownership, lifetimes) | Moderada (verbosa, mas previsível) |

## GC vs Ownership: O Trade-Off Central

### Garbage Collection em Java

Java usa um Garbage Collector que periodicamente pausa a aplicação para identificar e liberar memória não utilizada. Os GCs modernos (ZGC, Shenandoah) reduziram as pausas para sub-milissegundos, mas o trade-off permanece:

```java
import java.util.ArrayList;
import java.util.List;

public class ExemploGC {
    record Pedido(String cliente, double valor) {}

    public static void main(String[] args) {
        List<Pedido> pedidos = new ArrayList<>();

        // Milhões de alocações — o GC precisa gerenciar tudo
        for (int i = 0; i < 1_000_000; i++) {
            pedidos.add(new Pedido("Cliente " + i, Math.random() * 1000));
        }

        double total = pedidos.stream()
            .filter(p -> p.valor() > 500)
            .mapToDouble(Pedido::valor)
            .sum();

        System.out.printf("Total de pedidos acima de R$500: R$%.2f%n", total);
        // Objetos temporários do stream são coletados pelo GC eventualmente
    }
}
```

### Ownership em Rust

Rust libera memória deterministicamente quando o owner sai de escopo — sem pausas, sem overhead:

```rust
struct Pedido {
    cliente: String,
    valor: f64,
}

fn main() {
    let pedidos: Vec<Pedido> = (0..1_000_000)
        .map(|i| Pedido {
            cliente: format!("Cliente {i}"),
            valor: rand::random::<f64>() * 1000.0,
        })
        .collect();

    let total: f64 = pedidos
        .iter()
        .filter(|p| p.valor > 500.0)
        .map(|p| p.valor)
        .sum();

    println!("Total de pedidos acima de R$500: R${total:.2}");
} // pedidos e todos os Pedido são liberados aqui, instantaneamente
```

A diferença em memória é dramática: o vetor Java consome ~2x mais memória devido ao overhead de objetos na JVM (headers, padding, referências boxed).

## Exemplo Prático: API REST

Vamos comparar uma API REST simples para gerenciar tarefas.

### Java com Spring Boot

```java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@SpringBootApplication
@RestController
@RequestMapping("/tarefas")
public class TarefaApp {
    record Tarefa(Long id, String titulo, boolean concluida) {}
    record CriarTarefa(String titulo) {}

    private final Map<Long, Tarefa> tarefas = new ConcurrentHashMap<>();
    private final AtomicLong contador = new AtomicLong();

    @GetMapping
    public Collection<Tarefa> listar() {
        return tarefas.values();
    }

    @PostMapping
    public Tarefa criar(@RequestBody CriarTarefa dto) {
        long id = contador.incrementAndGet();
        Tarefa tarefa = new Tarefa(id, dto.titulo(), false);
        tarefas.put(id, tarefa);
        return tarefa;
    }

    @PutMapping("/{id}/concluir")
    public Tarefa concluir(@PathVariable Long id) {
        Tarefa atual = tarefas.get(id);
        if (atual == null) throw new RuntimeException("Tarefa não encontrada");
        Tarefa concluida = new Tarefa(atual.id(), atual.titulo(), true);
        tarefas.put(id, concluida);
        return concluida;
    }

    public static void main(String[] args) {
        SpringApplication.run(TarefaApp.class, args);
    }
}
```

### Rust com Axum

```rust
use axum::{
    extract::{Path, State},
    routing::{get, post, put},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{atomic::{AtomicU64, Ordering}, Arc, RwLock};

#[derive(Clone, Serialize)]
struct Tarefa {
    id: u64,
    titulo: String,
    concluida: bool,
}

#[derive(Deserialize)]
struct CriarTarefa {
    titulo: String,
}

type Db = Arc<RwLock<HashMap<u64, Tarefa>>>;

async fn listar(State(db): State<Db>) -> Json<Vec<Tarefa>> {
    let tarefas = db.read().unwrap();
    Json(tarefas.values().cloned().collect())
}

async fn criar(
    State(db): State<Db>,
    State(contador): State<Arc<AtomicU64>>,
    Json(dto): Json<CriarTarefa>,
) -> Json<Tarefa> {
    let id = contador.fetch_add(1, Ordering::Relaxed) + 1;
    let tarefa = Tarefa { id, titulo: dto.titulo, concluida: false };
    db.write().unwrap().insert(id, tarefa.clone());
    Json(tarefa)
}

async fn concluir(
    State(db): State<Db>,
    Path(id): Path<u64>,
) -> Json<Tarefa> {
    let mut tarefas = db.write().unwrap();
    let tarefa = tarefas.get_mut(&id).expect("Tarefa não encontrada");
    tarefa.concluida = true;
    Json(tarefa.clone())
}

#[tokio::main]
async fn main() {
    let db: Db = Arc::new(RwLock::new(HashMap::new()));
    let contador = Arc::new(AtomicU64::new(0));

    let app = Router::new()
        .route("/tarefas", get(listar).post(criar))
        .route("/tarefas/{id}/concluir", put(concluir))
        .with_state((db, contador));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap();
}
```

Ambos os frameworks são produtivos, mas as diferenças aparecem em produção. Confira nosso [tutorial de API REST com Axum](/tutoriais/api-rest-axum/) para um guia completo.

## Comparação de Performance

### Benchmarks de Servidor Web

| Métrica | Rust (Axum) | Java (Spring Boot) | Java (Quarkus Nativo) |
|---|---|---|---|
| **Requisições/s (JSON)** | ~850.000 | ~120.000 | ~250.000 |
| **Latência p99** | 0,8 ms | 8 ms | 3 ms |
| **Startup time** | < 5 ms | ~3.000 ms | ~50 ms (GraalVM) |
| **Memória idle** | ~5 MB | ~200 MB | ~40 MB |
| **Memória sob carga** | ~25 MB | ~500 MB | ~120 MB |
| **Tamanho do binário** | ~8 MB | ~40 MB (fat JAR) | ~60 MB (native) |

### O Custo Real da JVM

A JVM é uma plataforma extraordinária, mas tem um custo:

- **Startup lento**: a JVM precisa carregar classes, compilar JIT, aquecer caches. Isso é problemático para serverless (AWS Lambda, Cloud Functions)
- **Consumo de memória base**: mesmo uma aplicação "Hello World" em Spring consome ~150 MB
- **Pausas de GC**: embora menores com ZGC, ainda existem e afetam latência p99
- **Overhead de objetos**: cada objeto Java tem 12-16 bytes de header

Rust não tem nenhum desses custos. O binário inicia em milissegundos, consome poucos MB e tem latência completamente previsível.

### Onde Java se Recupera

Java brilha em:

- **Throughput sustentado**: após o warmup do JIT, a performance de Java pode se aproximar de código nativo
- **Profiling e observabilidade**: JFR, JMX, VisualVM — ferramentas de diagnóstico maduras
- **Ecossistema empresarial**: Spring, Hibernate, Kafka client, gRPC — tudo maduro e battle-tested

## Null Safety: Option vs Optional

Um dos maiores problemas em Java é o `NullPointerException`. Rust simplesmente não tem `null`:

```java
// Java: null é onipresente
String nome = mapa.get("chave"); // pode ser null
int tamanho = nome.length(); // NullPointerException em runtime!

// Com Optional (melhora, mas é opt-in)
Optional<String> nome = Optional.ofNullable(mapa.get("chave"));
int tamanho = nome.map(String::length).orElse(0);
```

```rust
// Rust: Option<T> é obrigatório — null não existe
let nome: Option<&str> = mapa.get("chave").map(|s| s.as_str());

// O compilador OBRIGA você a tratar o caso None
let tamanho = match nome {
    Some(n) => n.len(),
    None => 0,
};

// Ou de forma mais concisa:
let tamanho = nome.map_or(0, |n| n.len());
```

## Quando Usar Java

Escolha Java quando:

- **Projetos empresariais grandes**: equipes de 50+ desenvolvedores, onde a uniformidade importa
- **Ecossistema específico**: Kafka, Hadoop, Elasticsearch, Android (legado)
- **A equipe conhece Java profundamente**: reaprender uma linguagem tem custo
- **Spring Boot resolve o problema**: CRUD, APIs, microserviços convencionais
- **Hot reload importa**: desenvolvimento iterativo rápido com Spring DevTools

## Quando Usar Rust

Escolha Rust quando:

- **Serverless/containers**: onde startup time e memória são cobrados por uso
- **Microsserviços de alta performance**: baixa latência, alta concorrência
- **Sistemas embarcados ou IoT**: onde a JVM não cabe
- **Processamento de dados em tempo real**: sem pausas de GC
- **CLI tools**: binários estáticos sem dependências
- **WebAssembly**: Rust tem suporte muito superior para Wasm

## Conclusão e Recomendação

**Para novos microsserviços em 2026**, se performance, consumo de recursos e startup rápido são importantes (especialmente em ambientes cloud/serverless), Rust com Axum é a melhor escolha. Você economiza em custos de infraestrutura e ganha em confiabilidade.

**Para aplicações empresariais convencionais** onde o ecossistema Spring/Jakarta EE já resolve o problema e a equipe é proficiente em Java, continue com Java. O investimento em migrar não se justifica para CRUDs típicos.

**A tendência para 2026-2027** é clara: Rust está conquistando o nicho de "Java para microsserviços de alta performance", enquanto Java mantém seu domínio em aplicações empresariais tradicionais. Considere usar ambas no mesmo sistema: Java para lógica de negócios complexa e Rust para componentes críticos de performance.

---

## Veja Também

- [Tutorial: API REST com Axum](/tutoriais/api-rest-axum/) — Construa APIs completas em Rust
- [Rust vs Go: Qual Escolher em 2026](/artigos/rust-vs-go/) — Outra alternativa para backend
- [Rust vs Kotlin: Server-Side e Mobile](/artigos/rust-vs-kotlin/) — Compare com a alternativa moderna da JVM
- [Receita: Criar Servidor HTTP](/receitas/criar-servidor-http/) — Exemplo prático de servidor web
- [Instalação do Rust](/instalacao/) — Configure seu ambiente de desenvolvimento
- [Glossário Rust](/glossario/) — Termos como ownership, borrowing e traits
- [Kotlin Brasil](https://kotlin.dev.br) — Se você vem da JVM, conheça também Kotlin — a alternativa moderna ao Java
