Pontos Principais
-
Os desenvolvedores desejam distribuir suas aplicações Java de linha de comando como um único executável nativo.
-
O GraalVM pode compilar as aplicações Java em imagens nativas, mas possui algumas limitações.
-
O Picocli é uma biblioteca moderna para escrever aplicações de linha de comando (CLI) na JVM, que pode ajudar a resolver as limitações do GraalVM, inclusive no Windows.
-
A configuração do ferramental do GraalVM para criar imagens nativas no Windows não está bem documentada.
O sonho: executáveis Java
A linguagem de programação Go tornou-se popular pela facilidade em escrever aplicações de linha de comando. Pode haver muitas razões para isso, mas um aspecto em que o Go brilha é a capacidade de compilar um programa em um único arquivo executável nativo, facilitando a distribuição do programa.
Tradicionalmente, os programas Java são difíceis de distribuir porque exigem que uma Java Virtual Machine seja instalada na máquina de destino. É possível empacotar uma JVM com a aplicação, porém isso aumenta em aproximadamente 200MB o tamanho do pacote. As coisas estão caminhando na direção certa: o Java Module System (JPMS), introduzido no Java 9, inclui o utilitário jlink que permite que um aplicativo crie uma JRE customizada e minimizada, que pode ser tão pequeno quanto 30-40MB, e o Java 14 inclui o utilitário jpackage, que pode criar um instalador que inclua essa JRE mínima com seu aplicativo.
Ainda assim, para aplicações de linha de comando, um instalador não é o ideal. Idealmente, queremos distribuir nosso utilitário CLI como um executável nativo "real" sem um runtime empacotado. O GraalVM nos permite fazer isso com programas escritos em Java.
GraalVM
O GraalVM é uma máquina virtual universal que pode executar aplicativos escritos em linguagens como JavaScript, Python, Ruby, R, linguagens baseada na JVM, como Java, Scala, Clojure, Kotlin e linguagens baseadas em LLVM, como C e C ++. Um aspecto interessante é que o GraalVM permite que você misture linguagens de programação: programas parcialmente escritos em JavaScript, R, Python ou Ruby podem ser chamados a partir do Java e podem compartilhar dados entre si. Outro recurso é a capacidade de criar uma imagem nativa, e é isso que exploraremos neste artigo.
Imagens nativas do GraalVM
A Imagem Nativa do GraalVM permite compilar antecipadamente o código Java em um executável independente, chamado imagem nativa. Este executável inclui a aplicação, as bibliotecas, o JDK e não é executado na JVM, porém inclui os componentes necessários, como o gerenciamento de memória e o agendamento de thread em uma máquina virtual diferente, chamada "Substrate VM". Substrate VM é o nome dos componentes em tempo de execução (como o deoptimizer, garbage collector, thread scheduling, etc.). O programa resultante possui um tempo de inicialização mais rápido e menor sobrecarga de memória em tempo de execução em comparação com uma Java VM.
Limitações da imagem nativa
Para manter a implementação pequena e concisa, e também para permitir otimizações agressivas antecipadamente, a imagem nativa não suporta todos os recursos do Java. O conjunto completo de limitações está documentado no projeto no GitHub.
Duas limitações são de interesse particular:
Basicamente, para criar um binário independente, o compilador de imagem nativo precisa conhecer de antemão todas as classes de sua aplicação, suas dependências e os recursos que usam. Pacotes de reflection e resource geralmente requerem configuração. Veremos um exemplo disso mais adiante.
Picocli
O Picocli é uma biblioteca e framework para a criação de aplicações de linha de comando na JVM. Suporta Java, Groovy, Kotlin e Scala. Tem menos de 3 anos, mas se tornou bastante popular com mais de 500.000 downloads por mês. A linguagem Groovy usa o picocli para implementar sua DSL, a CliBuilder.
O Picocli pretende ser "a maneira mais fácil de criar aplicações avançadas de linha de comando que podem ser executadas dentro e fora da JVM". Também oferece impressão colorida no console, preenchimento automático com TAB, subcomandos e alguns recursos exclusivos em comparação às outras bibliotecas de CLI da JVM, como negatable options, grupos de argumentos compostos repetidos, subcomandos repetidos e manipulação sofisticada de argumentos. O código-fonte está em um único arquivo e opcionalmente pode ser incluído como fonte, para evitar a adição de uma dependência. O Picocli orgulha-se de sua extensa e meticulosa documentação.
O Picocli usa reflection, por isso é vulnerável às limitações de imagem nativa Java do GraalVM, porém oferece um processador de annotations que gera os arquivos de configuração que tratam dessa limitação no momento da compilação.
Um caso de uso concreto
Vamos ver um exemplo concreto de um utilitário de linha de comando que escreveremos em Java e vamos compilar em um único arquivo executável nativo. Ao longo do caminho, examinaremos alguns recursos da biblioteca Picocli que ajudam a tornar nosso utilitário fácil de usar.
Vamos construir um utilitário CLI de checksum, que aceita a opção nomeada -a ou --algorithm, e um parâmetro posicional, que é o arquivo cujo checksum deve ser computado.
Queremos que nossos usuários possam usar nosso utilitário de checksum Java assim como usam aplicativos escritos em C++ ou outras linguagens. Algo assim:
$ echo hi > hi.txt
$ checksum -a md5 ola.txt
764efa883dda1e11db47671c4a3bbd9e
$ checksum -a sha1 ola.txt
55ca6286e3e4f4fba5d0448333fa99fc5a404a73
Esse é o mínimo que esperamos de um aplicativo de linha de comando, mas não ficaremos satisfeitos com um aplicativo de denominador comum tão simples, queremos criar uma excelente aplicação de CLI que agrade a nossos usuários. O que isso significa e como fazemos?
Boas aplicações CLI são úteis
Fizemos uma troca: escolhendo uma interface de linha de comando (CLI) em vez de uma interface gráfica de usuário (GUI), nossa aplicação não é tão fácil de aprender a usar para novos usuários. Podemos compensar isso parcialmente, fornecendo uma boa ajuda on-line, por exemplo.
Nossa aplicação deve mostrar uma mensagem de ajuda quando o usuário solicitar ajuda com a opção -h ou --help ou quando ocorrer uma entrada inválida pelo usuário. Também deve mostrar as informações da versão quando solicitado com -V ou --version. Veremos como o picocli facilita isso.
Experiência do usuário
Podemos tornar nossa aplicação mais amigável usando cores em plataformas suportadas. Isso não apenas melhor aparenta, mas também reduz a carga cognitiva do usuário: o contraste destaca informações importantes como comandos, opções e parâmetros no texto ao redor.
A mensagem de ajuda gerada por um aplicativo picocli utiliza cores por padrão. Nosso exemplo de checksum é mais ou menos assim:
No geral, as aplicações devem produzir apenas cores quando usadas interativamente ao executar um script, não queremos que o arquivo de log esteja repleto de códigos de escape ANSI. Felizmente, o picocli cuida disso automaticamente. Isso nos leva ao próximo tópico: boas aplicações CLI são projetadas para serem combinadas com outros comandos.
Boas aplicações LI funcionam bem com outros
Stdout vs stderr
Muitas aplicações CLI usam os fluxos de E/S padrão para que possam ser combinados com outros utilitários. É nos detalhes que o diabo mora. Quando o usuário solicita ajuda, a aplicação deve exibir a mensagem de ajuda na saída padrão. Isso permite que os usuários direcionem a saída para outra ferramenta, como o grep.
Por outro lado, em uma entrada inválida, a mensagem de erro e a mensagem de ajuda devem ser impressas no fluxo de erros padrão: caso a saída do nosso programa seja usada como entrada para outro programa, não queremos que nossa mensagem de erro interrompa o processo.
Código de saída
Quando a aplicação termina, retorna um código de status de saída. Um código de saída zero é frequentemente usado para indicar sucesso e um código de saída diferente de zero geralmente indica uma falha de algum tipo.
Isso permite aos usuários encadear vários comandos usando //, sabendo que se algum comando da sequência falhar, a sequência inteira será interrompida.
Por padrão, o Picocli retorna 2 para entrada de usuário inválida, 1 quando ocorreu uma exceção na lógica de negócios do aplicativo e zero caso ocorra tudo bem. Obviamente, é fácil configurar outros códigos de saída em seu aplicativo, mas para o nosso exemplo de checksum, os valores padrões estão corretos.
Observe que a biblioteca picocli não chamará System.exit; apenas retorna um número inteiro e cabe ao aplicativo chamar System.exit ou não.
Código compacto
As seções acima descrevem algumas funcionalidades. Você pensaria que isso exigiria muito código para ser realizado, mas a maior parte do "comportamento padrão da CLI" é fornecida pelo Picocli. Em nossa aplicação, tudo o que precisamos fazer é definir nossas opções e parâmetros posicionais e implementar a lógica de negócios, tornando nossa classe Callable ou Runnable. Podemos inicializar a aplicação em nosso método main em uma linha de código:
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.io.File;
import java.math.BigInteger;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.util.concurrent.Callable;
@Command(name = "checksum", mixinStandardHelpOptions = true,
version = "checksum 4.0",
description = "Imprime o checksum (MD5 por padrão) de um arquivo para STDOUT.")
class CheckSum implements Callable<Integer> {
@Parameters(index = "0", arity = "1",
description = "O arquivo checksum deve ser calculado.")
private File file;
@Option(names = {"-a", "--algorithm"},
description = "MD5, SHA-1, SHA-256, ...")
private String algorithm = "MD5";
// este exemplo implementa Callable, analisando, e tratando erros
// e tratando solicitações do usuário para obter ajuda ou a versão
// pode ser feito com uma linha de código.
public static void main(String... args) {
int exitCode = new CommandLine(new CheckSum()).execute(args);
System.exit(exitCode);
}
@Override
public Integer call() throws Exception { // a lógica de negócio...
byte[] data = Files.readAllBytes(file.toPath());
byte[] digest = MessageDigest.getInstance(algorithm).digest(data);
String format = "%0" + (digest.length*2) + "x%n";
System.out.printf(format, new BigInteger(1, digest));
return 0;
}
}
Temos um exemplo real de um programa Java. Em seguida, vamos transformá-lo em um executável nativo.
Imagem nativa
Configuração do reflection
Mencionamos anteriormente que o compilador de imagem nativo tem algumas limitações: o reflection é suportado, mas requer configuração.
Isso afeta os aplicativos baseados em picocli: em tempo de execução, o picocli usa reflection para descobrir qualquer subcomando anotado com @Command e as opções de comando anotadas com @Option e @Parameters e parâmetros por posição.
Portanto, precisamos fornecer ao GraalVM um arquivo de configuração especificando todas as classes, métodos e campos anotados. Esse arquivo de configuração é mais ou menos assim:
[
{
"name" : "CheckSum",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"fields" : [
{ "name" : "algorithm" },
{ "name" : "file" }
]
},
{
"name" : "picocli.CommandLine$AutoHelpMixin",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"fields" : [
{ "name" : "helpRequested" },
{ "name" : "versionRequested" }
]
}
]
Isso rapidamente se torna bastante complicado para utilitários com muitas opções, mas felizmente não precisamos fazer isso manualmente.
Processador de Annotation Picocli
O módulo picocli-codegen inclui um processador de annotation que pode criar um modelo a partir das annotations picocli no momento da compilação ao invés de tempo de execução.
O processador de annotation gera arquivos de configuração Graal no projeto em: META-INF/native-image/picocli-generated/$project durante a compilação, para serem incluídos no jar da aplicação. Isso inclui arquivos de configuração para reflection, recursos e dynamic proxies. Ao incorporar esses arquivos de configuração, seu jar é instantaneamente ativado para a Graal. Na maioria dos casos, nenhuma configuração adicional é necessária ao gerar uma imagem nativa.
Como bônus, o processador de annotation mostra erros para as annotations ou atributos inválidos no momento que você compila, ao invés de exibir somente durante um teste em tempo de execução, assim resultando em ciclos de feedback mais curtos.
Portanto, tudo o que precisamos fazer é compilar nosso arquivo de origem CheckSum.java com o jar picocli-codegen no classpath:
Compilando a CheckSum.java e criando um checksum.jar no Linux. Substitua o separador ":" por ";" para que esses comandos funcionem no Windows.
mkdir classes
javac -cp .:picocli-4.2.0.jar:picocli-codegen-4.2.0.jar -d classes CheckSum.java
cd classes && jar -cvef CheckSum ../checksum.jar * && cd ..
Você pode ver os arquivos de configuração gerados no diretório META-INF/native-image/picocli-generated/ dentro do jar:
jar -tf checksum.jar
META-INF/
META-INF/MANIFEST.MF
CheckSum.class
META-INF/native-image/
META-INF/native-image/picocli-generated/
META-INF/native-image/picocli-generated/proxy-config.json
META-INF/native-image/picocli-generated/reflect-config.json
META-INF/native-image/picocli-generated/resource-config.json
Terminamos com a nossa aplicação. O próximo passo será criar uma imagem nativa!
Ferramental de imagem nativa do GraalVM
Para criar uma imagem nativa, precisamos instalar o GraalVM, garantir que o utilitário de imagem nativa esteja instalado e instalar o ferramental do compilador C/C++ no sistema operacional em que estamos construindo. Eu tive alguns problemas para fazer isso, por isso espero que as etapas abaixo esclareçam as coisas para outros desenvolvedores.
O desenvolvimento é 10% de inspiração e 90% configurando seu ambiente.
- Desenvolvedor desconhecido
Instale o GraalVM
Primeiro, instale a versão mais recente que até o momento deste artigo é a GraalVM, 20.0. A página de introdução do GraalVM é o melhor local para obter instruções atualizadas para a instalação do GraalVM em vários sistemas operacionais e containers.
Instale o Native Image Utility
O GraalVM vem com um utilitário gerador de imagem nativa. Nas versões mais recentes do GraalVM, é necessário fazer o download primeiro e instalar separadamente com a ferramenta Graal Updater:
Instalando o Utilitário Gerador de Imagem Nativa para Java 11 no Linux
gu install -L /path/to/native-image-installable-svm-java11-linux-amd64-20.0.0.jar
Esta etapa também se tornou necessária com a versão Windows do GraalVM desde a 20.0.
Para mais detalhes, consulte a seção Native Image do manual de referência do GraalVM.
Instale o ferramental do compilador
Ferramental do Compilador para Linux e MacOS
Para a compilação, a imagem nativa depende do ferramental local; portanto, no Linux e no MacOS, precisamos do glibc-devel, zlib-devel (arquivos de cabeçalho para a biblioteca C e zlib) e do gcc no nosso sistema.
Para fazer isso no Linux: sudo dnf install gcc glibc-devel zlib-devel or sudo apt-get install build-essential libz-dev.
No macOS, execute xcode-select --install.
Ferramental do compilador do Windows para Java 8
O GraalVM começou a oferecer suporte experimental para imagens nativas do Windows desde a versão 19.2.0.
O suporte ao Windows ainda é experimental e a documentação oficial é escassa em detalhes sobre imagens nativas no Windows. A partir da versão 19.3, o GraalVM suporta Java 8 e Java 11, e no Windows eles exigem ferramentas diferentes.
Para criar imagens nativas usando a versão Java 8 do GraalVM, você precisa do Microsoft Windows SDK para Windows 7 e .NET Framework 4, bem como os compiladores C do KB2519277. Você pode instalá-los usando chocolatey:
choco install windows-sdk-7.1 kb2519277
Em seguida (no prompt do cmd), ative o sdk-7.1:
"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd"
Isso inicia um novo prompt de comando, com o sdk-7.1 ativado. Execute todos os comandos subseqüentes nesta janela do prompt de comando. Isso funciona para todas as versões Java 8 do GraalVM de 19.2.0 a 20.0.
Ferramental do compilador do Windows para Java 11
Para criar imagens nativas usando a versão Java 11 do GraalVM (19.3.0 ou superior), você pode instalar a IDE do Visual Studio 2017 (certificando-se de incluir as ferramentas do Visual C++ para CMake) ou você pode instalar o Visual C Build Tools Workload para Visual Studio 2017 Build Tools usando chocolatey:
choco install visualstudio2017-workload-vctools
Após a instalação, configure o ambiente no prompt do cmd com este comando:
"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
DICA |
Se você instalou a IDE do Visual Studio 2017, substitua o BuildTools no comando acima por Community ou Enterprise, dependendo da sua versão do Visual Studio. |
Em seguida, execute a imagem nativa nessa janela do prompt de comando.
Criando uma imagem Nativa
O utilitário de imagem nativa pode pegar uma aplicação Java e compilá-lá em uma imagem nativa que pode ser executada como um executável nativo na plataforma em que é compilado. No Linux, isso pode ser feito assim:
Criando uma imagem nativa no Linux
$ /usr/lib/jvm/graalvm/bin/native-image \
-cp classes:picocli-4.2.0.jar --no-server \
--static -H:Name=checksum CheckSum
O utilitário de imagem nativa levará cerca de um minuto para ser concluído no meu laptop e produz uma saída como esta:
[checksum:1073] classlist: 3,124.74 ms, 1.14 GB
[checksum:1073] (cap): 2,885.31 ms, 1.14 GB
[checksum:1073] setup: 4,767.19 ms, 1.14 GB
[checksum:1073] (typeflow): 8,733.59 ms, 1.94 GB
[checksum:1073] (objects): 6,073.44 ms, 1.94 GB
[checksum:1073] (features): 313.28 ms, 1.94 GB
[checksum:1073] analysis: 15,384.41 ms, 1.94 GB
[checksum:1073] (clinit): 322.84 ms, 1.94 GB
[checksum:1073] universe: 793.02 ms, 1.94 GB
[checksum:1073] (parse): 2,191.69 ms, 1.94 GB
[checksum:1073] (inline): 2,064.62 ms, 2.13 GB
[checksum:1073] (compile): 14,960.43 ms, 2.73 GB
[checksum:1073] compile: 20,040.78 ms, 2.73 GB
[checksum:1073] image: 1,272.17 ms, 2.73 GB
[checksum:1073] write: 722.20 ms, 2.73 GB
[checksum:1073] [total]: 46,743.28 ms, 2.73 GB
No final, temos um executável nativo do Linux. Curiosamente, o binário nativo criado com o Java 11 do GraalVM é um pouco maior que o criado com o Java 8 do GraalVM:
-rwxrwxrwx 1 remko remko 14744296 Feb 19 09:51 java11-20.0/checksum*
-rwxrwxrwx 1 remko remko 12393600 Feb 19 09:48 java8-20.0/checksum*
Podemos ver que o binário tem o tamanho entre 12,4 à 14,7 MB. Podemos considerar isso grande ou pequeno, dependendo do que o compararmos. Pra mim, é um tamanho aceitável.
Vamos executar a aplicação para verificar se o mesmo funciona. Também podemos comparar os tempos de inicialização da execução da aplicação em uma JIT-based JVM normal com a da imagem nativa:
$ time java -cp classes:picocli-4.2.0.jar CheckSum hi.txt
764efa883dda1e11db47671c4a3bbd9e
real 0m0.415s ← startup is 415 millis with normal Java
user 0m0.609s
sys 0m0.313s
$ time ./checksum hi.txt
764efa883dda1e11db47671c4a3bbd9e
real 0m0.004s ← native image starts up in 4 millis
user 0m0.002s
sys 0m0.002s
Portanto, pelo menos agora no Linux podemos distribuir nossa aplicação Java como um único executável nativo. Qual é a história no Windows?
Imagem nativa no Windows
O suporte a imagens nativas no Windows tem alguns truques, portanto, veremos isso com mais detalhes.
Criando imagens nativas no Windows
Criar a imagem nativa em si não é um problema. Por exemplo:
Criando uma imagem nativa no Windows
C:\apps\graalvm-ce-java8-20.0.0\bin\native-image ^
-cp picocli-4.2.0.jar --static -jar checksum.jar
Obtemos uma saída semelhante do utilitário native-image.cmd no Windows como vimos no Linux, levando um tempo comparável e resultando em um executável ligeiramente menor de 11,3 MB para o Java 8 do GraalVM e 14,2 MB para um binário criado com o Java 11 do GraalVM.
Os binários funcionam bem, com uma diferença: não vemos cores ANSI no console. Vamos ver como arrumar isso.
Imagens nativas do Windows com saída colorida
Para obter cores ANSI no prompt de comando do Windows, precisamos usar a Jansi library. Infelizmente, o Jansi (a partir da versão 1.18) tem alguns problemas com falha na reprodução de saída colorida em uma imagem nativa do GraalVM. Para contornar isso, o picocli oferece uma biblioteca complementar Jansi, picocli-jansi-graalvm, permite que a biblioteca Jansi funcione corretamente em imagens nativas do GraalVM no Windows.
Alteramos o método principal para dizer ao Jansi para ativar a renderização de códigos de escape ANSI no Windows, assim:
//...
import picocli.jansi.graalvm.AnsiConsole;
//...
public class CheckSum implements Callable<Integer> {
// ...
public static void main(String[] args) {
int exitCode = 0;
// ativar cores no windows
try (AnsiConsole ansi = AnsiConsole.windowsInstall()) {
exitCode = new CommandLine(new CheckSum()).execute(args);
}
System.exit(exitCode);
}
}
E crie uma nova imagem nativa com este comando (observe que no GraalVM 19.3, tornou-se necessário citar os jars no classpath):
set GRAALVM_HOME=C:\apps\graalvm-ce-java11-20.0.0
%GRAALVM_HOME%\bin\native-image ^
-cp "picocli-4.2.0.jar;jansi-1.18.jar;picocli-jansi-graalvm-1.1.0.jar;checksum.jar" ^
picocli.nativecli.demo.CheckSum checksum
Agora temos cores no console do nosso aplicativo DOS:
É preciso um pouco de esforço extra, porém nosso aplicativo CLI nativo do Windows pode usar contraste de cores para fornecer uma experiência de usuário semelhante à do Linux.
O tamanho dos binários resultantes não mudou muito com a adição das bibliotecas Jansi: o build com Java 11 GraalVM gerou um binário de 14,3 MB, enquanto o build com Java 8 GraalVM gerou um binário de 11,3 MB.
Executando imagens nativas no Windows
Estamos quase terminando, mas há mais uma pegadinha que não é aparente logo de cara.
O binário nativo que acabamos de criar funciona bem na máquina em que acabamos de construí-lo, mas quando você o executa em uma máquina Windows diferente, você pode ver o seguinte erro:
Acontece que nossa imagem nativa precisa do msvcr100.dll do VS C ++ Redistributable 2010. Essa dll pode ser colocada no mesmo diretório que o exe ou no C:\Windows\System32. Há um trabalho em andamento para tentar melhorar isso.
Com o GraalVM para Java 11, obtemos um erro semelhante, exceto pelo relato de uma DLL ausente diferente, o VCRUNTIME140.dll:
Por enquanto, teremos que distribuir essas DLLs com nossa aplicação ou solicitar aos usuários que baixem e instalem o Microsoft Visual C++ 2015 Redistributable Update 3 RC para obter o VCRUNTIME140.dll para imagens nativas baseadas em Java 11 ou o Microsoft Visual C++ 2010 SP1 Redistributable Package (x64) para obter o msvcr100.dll para imagens nativas baseadas em Java 8.
O GraalVM não suporta cross-compilation, embora talvez possa no futuro. Por enquanto, precisamos compilar no Linux para obter um executável Linux, compilar no MacOS para obter um executável do MacOS e compilar no Windows para obter um executável do Windows.
Conclusão
As aplicações de linha de comando são o caso de uso canônico para imagens nativas do GraalVM: agora podemos desenvolver em Java (ou outra linguagem JVM) e distribuir nossas aplicações CLI como um único executável nativo relativamente pequeno. (Exceto no Windows, no qual podemos precisar distribuir uma DLL em tempo de execução adicional.) A inicialização rápida e o espaço reduzido de memória são ótimos bônus.
As imagens nativas do GraalVM têm algumas limitações e as aplicações podem precisar fazer algum trabalho antes de poderem se transformar em uma imagem nativa.
O Picocli facilita o desenvolvimento de aplicações de linha de comando em muitos idiomas baseados em JVM e fornece vários extras para transformar aplicações CLI em imagens nativas sem problemas.
Experimente o Picocli e o GraalVM em seu próxima aplicação de linha de comando!
Sobre o Autor
Remko Popma é desenvolvedor de algoritmos na SMBC Nikko Securities durante o dia e é autor de picocli à noite. Popma também é um hacker de desempenho do Log4j 2. Ele trabalhou a frente do Log4j 2 para não utilizar o Garbage Collector e também contribuiu com o Async Loggers, que possui uma taxa de transferência 10x mais alta e ordens de magnitude mais baixas em cenários multithread em comparação com os atuais pacotes de log líderes de mercado log4j-1.xe logback. Popma é responsável pela maioria das melhorias de desempenho desde o Log4j 2.0-beta4. Por diversão, ele gosta de aprender coisas novas: tecnologias de desempenho, concorrência, escalabilidade, baixa latência, IA, aprendizado de máquina, criptografia, bitcoin, e criptomoeda. Popma pode ser encontrado por @RemkoPopma