Cruzando um ouriço com um ouriço: OpenJDK-11 + GraalVM

Olá Habr! À luz das notícias não mais recentes sobre a política da Oracle em relação ao licenciamento Java, a questão de deixar as versões do Oracle para o OpenJDK está se tornando cada vez mais aguda. O Odanko no OracleLabs faz muito tempo uma coisa muito legal chamada GraalVM , que é um compilador JIT legal escrito em Java, além de um tempo de execução para executar código em linguagens como JavaScript, Ruby, Python, C, C ++, Scala, Kotlin, R. Clojure. Impressionante, certo? Mas não sobre o frescor do ambiente poliglota, quero lhe dizer. Falaremos sobre as dificuldades de combinar a última montagem do grail no ecossistema OpenJDK 11 e um pouco sobre desempenho, um pouco ...

Primeiro foi a palavra


A história do meu conhecimento da graalvm começou no coringa em 2017. Lá Chris Seaton falou detalhadamente sobre os componentes internos do compilador e mostrou a mágica da compilação AOT usando a imagem nativa da entrega do grail como exemplo (isso é uma piada que compila seu código Java em um binário nativo).

Após esse relatório, eu treinei por muito tempo na compilação do binário nativo do meu projeto de estimação, coloque muletas e ancinhos para obter reflexão em todos os lugares (não está tudo bem!) E finalmente me deparei com problemas não resolvidos com IO (algo lá não decolou com um tratador, agora não me lembro o quê). Cuspir enquanto na imagem nativa :-(

Ano de 2018, o mesmo coringa e o mesmo graalvm em um relatório super detalhado de Oleg Shelaev sobre a AOT.

Em relatórios e apresentações, tudo parece tão legal, e também há um projeto para animais de estimação no disco ... é hora de descobrir o terminal, levar um candidato ao lançamento do Graal e entrar em batalha! Vamos tocar o jit.

Olá mundo


Tocando e chutando um pouco JIT fresco (no momento em que este artigo foi escrito é a versão ce-1.0.0-rc14 ), usaremos o exemplo de um código para testar o desempenho no site https://graalvm.org - nosso primeiro exemplo.

Que projeto java (mesmo o Hello World ) está fazendo sem nenhum sistema de compilação? É isso mesmo, apenas o que javac aprende a cozinhar. Javak, não vamos estudar para cozinhar, deixe o maven guiar o javak.

Portanto, conheça pom.xml:

pom.xml
<?xml version="1.0"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <!--<packaging>jar</packaging>--> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>my-app</name> <url>http://maven.apache.org</url> <properties> <java.version>11</java.version> <graalvm.version>1.0.0-rc14</graalvm.version> </properties> <profiles> <profile> <id>jdk11</id> <activation> <jdk>11</jdk> </activation> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.10</version> <executions> <execution> <id>copy</id> <phase>process-test-classes</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.mycompany.app.App</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> </profile> </profiles> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>${java.version}</release> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.graalvm.compiler</groupId> <artifactId>compiler</artifactId> <version>${graalvm.version}</version> </dependency> <dependency> <groupId>org.graalvm.truffle</groupId> <artifactId>truffle-api</artifactId> <version>${graalvm.version}</version> </dependency> <dependency> <groupId>org.graalvm.sdk</groupId> <artifactId>graal-sdk</artifactId> <version>${graalvm.version}</version> </dependency> <dependency> <groupId>org.graalvm.js</groupId> <artifactId>js</artifactId> <version>${graalvm.version}</version> </dependency> <dependency> <groupId>org.graalvm.js</groupId> <artifactId>js-scriptengine</artifactId> <version>${graalvm.version}</version> </dependency> </dependencies> </project> 


A estrutura do arquivo do projeto é assim:



Código de classe com.mycompany.app.App (exemplo de copiar e colar de graalvm.org):

App.java
  package com.mycompany.app; public class App { static final int ITERATIONS = Math.max(Integer.getInteger("iterations", 1), 1); public static void main(String[] args) { String sentence = String.join(" ", args); for (int iter = 0; iter < ITERATIONS; iter++) { if (ITERATIONS != 1) System.out.println("-- iteration " + (iter + 1) + " --"); long total = 0, start = System.currentTimeMillis(), last = start; for (int i = 1; i < 10_000_000; i++) { total += sentence.chars().filter(Character::isUpperCase).count(); if (i % 1_000_000 == 0) { long now = System.currentTimeMillis(); System.out.printf("%d (%d ms)%n", i / 1_000_000, now - last); last = now; } } System.out.printf("total: %d (%d ms)%n", total, System.currentTimeMillis() - start); } } } 


Código module-info.java :

module-info.java
  module com.mycompany.app {} 


Hmm, vazio ... Mas está vazio pelo motivo que quero mostrar como o java personalizado (sobre o java personalizado um pouco mais tarde) jurará com módulos não declarados que nosso aplicativo precisa.

A primeira panqueca ...


não irregular. Vamos montar nosso projeto e lançá-lo. Colocando desta maneira:

 mvn clean package 


Lançamos:

 $JAVA_HOME/bin/java -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler -Diterations=10 -jar target/my-app-1.0-SNAPSHOT.jar In 2017 I would like to run ALL languages in one VM. 

Existem dois pontos importantes aqui: JVMCI é uma coisa experimental que apareceu em Java desde a versão 9, por isso precisamos:

  • ativar opções experimentais de máquina virtual -
      -XX: + UnlockEperimentalVMOptions 
  • habilite a máquina virtual muito experimental (a partir das nove, o grail está no openjdk, não na versão mais recente, mas ainda assim) - -XX: + UseJVMCICompiler

Resultado do lançamento
- iteração 1 - 1 (1466 ms)
2 (461 ms)
3 (463 ms)
4 (138 ms)
5 (151 ms)
6 (159 ms)
7 (266 ms)
8 (128 ms)
9 (144 ms)
total: 69999993 (3481 ms)
- iteração 2 - 1 (233 ms)
2 (169 ms)
3 (121 ms)
4 (205 ms)
5 (170 ms)
6 (152 ms)
7 (227 ms)
8 (158 ms)
9 (108 ms)
total: 69999993 (1644 ms)
- iteração 3 - 1 (98 ms)
2 (102 ms)
3 (98 ms)
4 (102 ms)
5 (95 ms)
6 (96 ms)
7 (101 ms)
8 (95 ms)
9 (97 ms)
total: 69999993 (990 ms)
- iteração 4-1 (109 ms)
2 (114 ms)
3 (97 ms)
4 (98 ms)
5 (100 ms)
6 (103 ms)
7 (125 ms)
8 (108 ms)
9 (100 ms)
total: 69999993 (1056 ms)
- iteração 5-1 (98 ms)
2 (100 ms)
3 (105 ms)
4 (97 ms)
5 (95 ms)
6 (99 ms)
7 (95 ms)
8 (123 ms)
9 (98 ms)
total: 69999993 (1010 ms)
- iteração 6-1 (99 ms)
2 (95 ms)
3 (102 ms)
4 (99 ms)
5 (96 ms)
6 (100 ms)
7 (99 ms)
8 (99 ms)
9 (104 ms)
total: 69999993 (993 ms)
- iteração 7-1 (100 ms)
2 (104 ms)
3 (95 ms)
4 (96 ms)
5 (97 ms)
6 (95 ms)
7 (94 ms)
8 (108 ms)
9 (108 ms)
total: 69999993 (1000 ms)
- iteração 8-1 (100 ms)
2 (106 ms)
3 (99 ms)
4 (95 ms)
5 (97 ms)
6 (97 ms)
7 (101 ms)
8 (99 ms)
9 (101 ms)
total: 69999993 (1012 ms)
- iteração 9-1 (105 ms)
2 (97 ms)
3 (98 ms)
4 (96 ms)
5 (99 ms)
6 (96 ms)
7 (94 ms)
8 (98 ms)
9 (105 ms)
total: 69999993 (993 ms)
- iteração 10-1 (107 ms)
2 (98 ms)
3 (99 ms)
4 (100 ms)
5 (97 ms)
6 (101 ms)
7 (98 ms)
8 (103 ms)
9 (105 ms)
total: 69999993 (1006 ms)

O que vemos aqui? E vemos que a primeira iteração é a mais longa (3,5 segundos), esse JIT está esquentando. E então tudo é mais ou menos suave (dentro de um segundo).

Mas e se dermos a Java uma versão nova do graal? Mal disse o que fez:

 java -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler -Diterations=10 --module-path=target/lib --upgrade-module-path=target/lib/compiler-1.0.0-rc14.jar -jar target/my-app-1.0-SNAPSHOT.jar In 2017 I would like to run ALL languages in one VM. 

Resultado de lançamento de graal fresco
- iteração 1 - 1 (1789 ms)
2 (547 ms)
3 (313 ms)
4 (87 ms)
5 (88 ms)
6 (87 ms)
7 (83 ms)
8 (92 ms)
9 (87 ms)
total: 69999993 (3259 ms)
- iteração 2 - 1 (241 ms)
2 (161 ms)
3 (152 ms)
4 (195 ms)
5 (136 ms)
6 (129 ms)
7 (154 ms)
8 (176 ms)
9 (109 ms)
total: 69999993 (1553 ms)
- iteração 3 - 1 (109 ms)
2 (103 ms)
3 (113 ms)
4 (172 ms)
5 (141 ms)
6 (148 ms)
7 (111 ms)
8 (102 ms)
9 (101 ms)
total: 69999993 (1211 ms)
- iteração 4-1 (96 ms)
2 (96 ms)
3 (104 ms)
4 (98 ms)
5 (96 ms)
6 (97 ms)
7 (98 ms)
8 (96 ms)
9 (95 ms)
total: 69999993 (972 ms)
- iteração 5-1 (97 ms)
2 (93 ms)
3 (99 ms)
4 (97 ms)
5 (97 ms)
6 (97 ms)
7 (95 ms)
8 (98 ms)
9 (94 ms)
total: 69999993 (965 ms)
- iteração 6-1 (96 ms)
2 (95 ms)
3 (96 ms)
4 (99 ms)
5 (102 ms)
6 (94 ms)
7 (99 ms)
8 (115 ms)
9 (109 ms)
total: 69999993 (1001 ms)
- iteração 7-1 (98 ms)
2 (96 ms)
3 (99 ms)
4 (98 ms)
5 (118 ms)
6 (98 ms)
7 (95 ms)
8 (99 ms)
9 (116 ms)
total: 69999993 (1017 ms)
- iteração 8-1 (95 ms)
2 (99 ms)
3 (99 ms)
4 (106 ms)
5 (101 ms)
6 (101 ms)
7 (93 ms)
8 (97 ms)
9 (108 ms)
total: 69999993 (993 ms)
- iteração 9-1 (102 ms)
2 (95 ms)
3 (97 ms)
4 (125 ms)
5 (94 ms)
6 (101 ms)
7 (100 ms)
8 (95 ms)
9 (96 ms)
total: 69999993 (1008 ms)
- iteração 10-1 (97 ms)
2 (97 ms)
3 (99 ms)
4 (112 ms)
5 (102 ms)
6 (96 ms)
7 (96 ms)
8 (98 ms)
9 (96 ms)
total: 69999993 (988 ms)

O resultado, como vemos, não é muito diferente.

Eu esqueci Bem, não tentamos executar a mesma coisa sem o novo compilador JIT. Nós faremos:

 java -Diterations=10 -jar target/my-app-1.0-SNAPSHOT.jar In 2017 I would like to run ALL languages in one VM. 

Resultado sem um compilador JIT newfangled
- iteração 1 - 1 (372 ms)
2 (271 ms)
3 (337 ms)
4 (391 ms)
5 (328 ms)
6 (273 ms)
7 (239 ms)
8 (271 ms)
9 (250 ms)
total: 69999993 (2978 ms)
- iteração 2 - 1 (242 ms)
2 (253 ms)
3 (253 ms)
4 (240 ms)
5 (245 ms)
6 (275 ms)
7 (273 ms)
8 (263 ms)
9 (234 ms)
total: 69999993 (2533 ms)
- iteração 3 - 1 (237 ms)
2 (235 ms)
3 (234 ms)
4 (246 ms)
5 (242 ms)
6 (238 ms)
7 (244 ms)
8 (243 ms)
9 (253 ms)
total: 69999993 (2414 ms)
- iteração 4-1 (244 ms)
2 (249 ms)
3 (245 ms)
4 (243 ms)
5 (232 ms)
6 (256 ms)
7 (321 ms)
8 (303 ms)
9 (249 ms)
total: 69999993 (2599 ms)
- iteração 5-1 (246 ms)
2 (242 ms)
3 (248 ms)
4 (256 ms)
5 (280 ms)
6 (233 ms)
7 (235 ms)
8 (266 ms)
9 (246 ms)
total: 69999993 (2511 ms)
- iteração 6-1 (292 ms)
2 (368 ms)
3 (339 ms)
4 (251 ms)
5 (267 ms)
6 (259 ms)
7 (289 ms)
8 (262 ms)
9 (357 ms)
total: 69999993 (3058 ms)
- iteração 7-1 (284 ms)
2 (258 ms)
3 (248 ms)
4 (247 ms)
5 (266 ms)
6 (247 ms)
7 (242 ms)
8 (314 ms)
9 (265 ms)
total: 69999993 (2656 ms)
- iteração 8-1 (239 ms)
2 (238 ms)
3 (257 ms)
4 (282 ms)
5 (244 ms)
6 (261 ms)
7 (253 ms)
8 (295 ms)
9 (256 ms)
total: 69999993 (2575 ms)
- iteração 9-1 (273 ms)
2 (243 ms)
3 (239 ms)
4 (240 ms)
5 (250 ms)
6 (285 ms)
7 (266 ms)
8 (285 ms)
9 (264 ms)
total: 69999993 (2617 ms)
- iteração 10-1 (245 ms)
2 (264 ms)
3 (258 ms)
4 (253 ms)
5 (239 ms)
6 (260 ms)
7 (251 ms)
8 (250 ms)
9 (256 ms)
total: 69999993 (2538 ms)

O resultado é diferente e decente.

O C2 não fornece otimizações em partes importantes do código - cada iteração ao mesmo tempo.

Graal é capaz de otimizar partes importantes do código e, a longo prazo, fornece um bom desempenho.

E daí?


Talvez essa seja a principal pergunta que você precisa se perguntar e a outros (membros da equipe) quando queremos adicionar um novo recurso ao projeto, uma nova ferramenta, uma nova máquina virtual, um novo JIT ...

Minha história, como foi escrita acima, começou com o Joker 2017, depois houve longas tentativas de dominar o AOT e agora sinto o gosto do JIT para Java em Java.

Um projeto de estimação no disco é um tipo de mecanismo de processo de negócios, em que os processos são desenhados pelos desenvolvedores de aplicativos na interface do usuário do navegador e têm a capacidade de gravar scripts em JS que serão executados na JVM.

Nas versões futuras do Java que prometem remover o nashorn, o GraalVM está gradualmente se aproximando do lançamento ...

Bem, a resposta para a pergunta é esta:

  1. queremos que um tempo de execução execute JS (e não apenas)
  2. quer velocidade jit
  3. queremos que o lançamento de aplicativos para projetos de animais de estimação apareça como antes no 8-ke (sem nenhum
      --module-path 
    e
      --upgrade-module-path 
    mas com um conjunto de graal fresco)

Jlink


Os dois primeiros pontos na lista de respostas à pergunta acima são claros o suficiente, vamos lidar com a última.

O fato é que os desenvolvedores-administradores-devops são preguiçosos e não gostam de fazer um trabalho extra (eu também gosto disso), eles tentam automatizar tudo e agrupá-lo em um pacote pronto que pode ser executado como um binário simples. Bem, há um problema, vamos resolvê-lo.

Uma ferramenta relativamente nova do mundo do Java 9+ vem em nosso auxílio, e seu nome é jlink . Estamos tentando compactar nosso aplicativo com todas as bibliotecas necessárias em um pacote:

 jlink --module-path target/classes:target/lib:$JAVA_HOME/jmods --add-modules com.mycompany.app --launcher app=com.mycompany.app/com.mycompany.app.App --compress 2 --no-header-files --no-man-pages --strip-debug --output test 

Quantos parâmetros de todos os tipos, descrevemos os principais:
  •   --module-path target / classes: target / lib: $ JAVA_HOME / jmods 
  •   --add-modules com.mycompany.app 
  •   --launcher app = com.mycompany.app / com.mycompany.app.App 
  •   - teste de produção 

Você pode perguntar ao tio do Google sobre outros parâmetros, todos eles com o objetivo de reduzir o tamanho total do pacote.

Vejamos o resultado:



Dentro de test / bin / app, há um script sh simples que inicia nosso aplicativo no Java que fica ao lado do aplicativo:

 #!/bin/sh JLINK_VM_OPTIONS="-Diterations=10" #     ,       DIR=`dirname $0` $DIR/java $JLINK_VM_OPTIONS -m com.mycompany.app/com.mycompany.app.App $@ 

Execute test / bin / app no C2:

 ./test/bin/app In 2017 I would like to run ALL languages in one VM. 

Resultado
- iteração 1 - 1 (315 ms)
2 (231 ms)
3 (214 ms)
4 (297 ms)
5 (257 ms)
6 (211 ms)
7 (217 ms)
8 (245 ms)
9 (222 ms)
total: 69999993 (2424 ms)
- iteração 2 - 1 (215 ms)
2 (215 ms)
3 (223 ms)
4 (224 ms)
5 (217 ms)
6 (208 ms)
7 (208 ms)
8 (222 ms)
9 (222 ms)
total: 69999993 (2164 ms)
- iteração 3 - 1 (206 ms)
2 (226 ms)
3 (234 ms)
4 (211 ms)
5 (212 ms)
6 (213 ms)
7 (210 ms)
8 (245 ms)
9 (223 ms)
total: 69999993 (2216 ms)
- iteração 4-1 (222 ms)
2 (233 ms)
3 (220 ms)
4 (222 ms)
5 (221 ms)
6 (219 ms)
7 (222 ms)
8 (216 ms)
9 (220 ms)
total: 69999993 (2215 ms)
- iteração 5-1 (231 ms)
2 (230 ms)
3 (221 ms)
4 (226 ms)
5 (227 ms)
6 (223 ms)
7 (215 ms)
8 (216 ms)
9 (219 ms)
total: 69999993 (2234 ms)
- iteração 6-1 (227 ms)
2 (218 ms)
3 (221 ms)
4 (254 ms)
5 (222 ms)
6 (212 ms)
7 (214 ms)
8 (222 ms)
9 (222 ms)
total: 69999993 (2241 ms)
- iteração 7-1 (217 ms)
2 (225 ms)
3 (222 ms)
4 (223 ms)
5 (227 ms)
6 (221 ms)
7 (219 ms)
8 (226 ms)
9 (219 ms)
total: 69999993 (2217 ms)
- iteração 8-1 (218 ms)
2 (242 ms)
3 (219 ms)
4 (218 ms)
5 (224 ms)
6 (226 ms)
7 (223 ms)
8 (220 ms)
9 (219 ms)
total: 69999993 (2228 ms)
- iteração 9-1 (234 ms)
2 (218 ms)
3 (217 ms)
4 (217 ms)
5 (225 ms)
6 (222 ms)
7 (216 ms)
8 (226 ms)
9 (214 ms)
total: 69999993 (2212 ms)
- iteração 10-1 (226 ms)
2 (230 ms)
3 (215 ms)
4 (238 ms)
5 (225 ms)
6 (218 ms)
7 (218 ms)
8 (215 ms)
9 (228 ms)
total: 69999993 (2233 ms)

Agora no graalvm (definindo os sinalizadores necessários para executar na variável JLINK_VM_OPTIONS ):

teste / bin / app
 #!/bin/sh JLINK_VM_OPTIONS="-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler -Diterations=10" DIR=`dirname $0` $DIR/java $JLINK_VM_OPTIONS -m com.mycompany.app/com.mycompany.app.App $@ 


Resultado:

 Error occurred during initialization of boot layer java.lang.module.FindException: Module jdk.internal.vm.ci not found 

Bem, navegamos ... E agora lembramos que estamos trabalhando com o java 11 em um ambiente modular, estamos construindo o aplicativo como um módulo, mas eles não disseram a ninguém sobre os módulos usados. É hora de consertar isso.

Nova versão do module-info.java:

 module com.mycompany.app { requires jdk.internal.vm.compiler; requires org.graalvm.sdk; requires org.graalvm.truffle; requires transitive org.graalvm.js; requires transitive org.graalvm.js.scriptengine; } 

Nós coletamos , excluímos o diretório de teste, link .

Resultado:

 Error: automatic module cannot be used with jlink: icu4j from file:///home/slava/JavaProjects/graal-js-jdk11-maven-demo/target/lib/icu4j-62.1.jar 

Que tipo de "módulo automático kennote bi uzd"? E esse jlink nos diz que a biblioteca icu4j não contém module-info.class. O que é necessário para que essa classe apareça dentro da lib especificada:

  • entenda a lista de módulos usados ​​por any e crie module-info.java , defina todos os pacotes que devem ser visíveis de fora
  • compile module-info.java para
  • coloque o module-info.java compilado no dzharnik com qualquer

Vamos lá!

O arquivo module-info.java com todo o seu conteúdo irá gerar o utilitário jdeps a partir do openjdk-11 para nós:



Nós compilamos module-info.java para icu4j:



Atualizamos o dzharnik pressionando module-info.class :

 $JAVA_HOME/bin/jar uf target/lib/icu4j-62.1.jar -C target/modules module-info.class 

Link , corra .

Resultado
- iteração 1 - 1 (1216 ms)
2 (223 ms)
3 (394 ms)
4 (138 ms)
5 (116 ms)
6 (102 ms)
7 (120 ms)
8 (106 ms)
9 (110 ms)
total: 69999993 (2619 ms)
- iteração 2 - 1 (166 ms)
2 (133 ms)
3 (142 ms)
4 (157 ms)
5 (119 ms)
6 (134 ms)
7 (153 ms)
8 (95 ms)
9 (85 ms)
total: 69999993 (1269 ms)
- iteração 3 - 1 (86 ms)
2 (81 ms)
3 (87 ms)
4 (83 ms)
5 (85 ms)
6 (100 ms)
7 (87 ms)
8 (83 ms)
9 (85 ms)
total: 69999993 (887 ms)
- iteração 4-1 (84 ms)
2 (86 ms)
3 (88 ms)
4 (91 ms)
5 (85 ms)
6 (88 ms)
7 (87 ms)
8 (85 ms)
9 (85 ms)
total: 69999993 (864 ms)
- iteração 5-1 (94 ms)
2 (86 ms)
3 (84 ms)
4 (83 ms)
5 (85 ms)
6 (86 ms)
7 (84 ms)
8 (84 ms)
9 (83 ms)
total: 69999993 (854 ms)
- iteração 6-1 (83 ms)
2 (89 ms)
3 (87 ms)
4 (87 ms)
5 (86 ms)
6 (86 ms)
7 (91 ms)
8 (86 ms)
9 (85 ms)
total: 69999993 (865 ms)
- iteração 7-1 (87 ms)
2 (86 ms)
3 (88 ms)
4 (90 ms)
5 (91 ms)
6 (87 ms)
7 (85 ms)
8 (85 ms)
9 (86 ms)
total: 69999993 (868 ms)
- iteração 8-1 (84 ms)
2 (85 ms)
3 (86 ms)
4 (84 ms)
5 (84 ms)
6 (88 ms)
7 (85 ms)
8 (86 ms)
9 (86 ms)
total: 69999993 (852 ms)
- iteração 9-1 (83 ms)
2 (85 ms)
3 (84 ms)
4 (85 ms)
5 (89 ms)
6 (85 ms)
7 (88 ms)
8 (86 ms)
9 (83 ms)
total: 69999993 (850 ms)
- iteração 10-1 (83 ms)
2 (84 ms)
3 (83 ms)
4 (82 ms)
5 (85 ms)
6 (83 ms)
7 (84 ms)
8 (94 ms)
9 (93 ms)
total: 69999993 (856 ms)

Viva! Nós conseguimos! Agora, temos um aplicativo banido na forma de um script sh em execução com nosso Java, com todos os módulos necessários (incluindo graalvm fresco), com preferência e jovens senhoras.

PS


Java não fica entediado e dá novos alimentos para a mente a cada lançamento. Experimente novos recursos, experimente, compartilhe experiências. Espero escrever um artigo em breve sobre como bani parte do projeto pet com o graal (existem vert.x, assincronismo e js-scripts - será interessante).

E, no entanto ... este é meu primeiro artigo sobre Habré, - por favor, não bata com força.

Source: https://habr.com/ru/post/pt445978/


All Articles