Executando verificação de segurança...
2

jbundle: distribuindo aplicações JVM como binário sem o inferno do GraalVM

Quem trabalha com JVM (Java, Clojure, Kotlin, Scala) conhece o problema: você quer distribuir sua aplicação como um binário único, sem exigir que o usuário tenha Java instalado.

A "solução" oficial é o GraalVM native-image. Na teoria, compila pra binário nativo. Na prática:

  • Build de 10+ minutos
  • Configuração de reflection pra cada lib que usa reflection (spoiler: quase todas)
  • Libs incompatíveis que simplesmente não funcionam
  • Toolchain complexo que quebra entre versões

Cansei disso. Criei o jbundle.

O que é

Uma ferramenta em Rust que empacota sua aplicação JVM + um runtime mínimo (via jlink) num binário único e autocontido.

projeto/jar → jbundle → binário único (roda em qualquer lugar)

Sem Java instalado no destino. Sem configuração de reflection. Sem libs incompatíveis. Funciona exatamente como funciona no seu ambiente de dev.

Como funciona

  1. Detecta o build system (deps.edn, project.clj, pom.xml, build.gradle)
  2. Builda o JAR
  3. Baixa JDK da Adoptium (fica em cache)
  4. Detecta módulos necessários com jdeps
  5. Cria runtime mínimo com jlink (~30-50 MB)
  6. Empacota tudo num binário multi-camada

O binário resultante usa cache por hash de conteúdo. Atualizou só o código da app? O runtime cached é reaproveitado.

Comparativo honesto

jbundleGraalVM native-image
Compatibilidade100% JVMRequer config de reflection, libs não suportadas
Tempo de buildRápido (jlink + packaging)Lento (compilação AOT)
Tamanho do binário~30-50 MB~20-40 MB
Startup (após 1ª execução)~200-350ms~10-50ms
Primeira execuçãoExtrai + gera CDS (~2-5s extra)Instantâneo
SetupSó o jbundleGraalVM + native-image + configs
DebugTooling JVM padrãoLimitado

Sim, GraalVM ganha em startup e tamanho. Mas jbundle ganha em tudo que importa no dia a dia: compatibilidade total, build rápido, zero configuração.

Uso

# A partir de um projeto Clojure
jbundle build --input ./meu-projeto --output ./dist/app

# A partir de um projeto Java/Kotlin/Scala
jbundle build --input ./meu-projeto --output ./dist/app

# A partir de um JAR pronto
jbundle build --input ./target/app.jar --output ./dist/app

# Executar — não precisa de Java instalado
./dist/app

Performance de startup
A primeira execução é mais lenta (~2-5s) porque extrai as camadas e gera o AppCDS cache. Execuções subsequentes usam o cache:

  • Profile Server: ~400-600ms (padrão, pra serviços)
  • RaC (Linux): ~10-50ms (checkpoint/restore, quase nativo)
    Instalação
git clone https://github.com/avelino/jbundle.git
cd jbundle
cargo install --path .

Por que Rust?
Porque tooling de build precisa ser rápido e sem dependências. Irônico usar Rust pra resolver um problema de distribuição Java? Talvez. Mas funciona.

Link: https://github.com/avelino/jbundle

Se você já sofreu com GraalVM, experimenta. Se funcionar pro seu caso de uso, uma star ajuda o projeto a ganhar visibilidade.

Feedbacks e contribuições são bem-vindos — especialmente suporte a Windows, que ainda não tá implementado.

Carregando publicação patrocinada...