
Seis meses se passaram, o que significa que é hora de instalar o novo Java ! Foi uma longa jornada, e poucos chegaram ao fim. As linhas brutas caíram de PECs interessantes, mas falaremos sobre o resto.
Como está indo tudo
O lançamento da nova versão do Java ocorre de acordo com o novo ciclo de lançamento "acelerado", com duração de cerca de seis meses. As datas exatas são definidas na página do projeto . Havia várias fases principais para o JDK 12:
- 13/12/2018 - A primeira fase da desaceleração (neste momento, um fork é feito a partir da ramificação principal no repositório);
- 17/01/2019 - A segunda fase de desaceleração (para concluir tudo o que é possível);
- 07/02/2019 - Liberar candidato (apenas os bugs mais importantes são corrigidos);
- 19/03/2019 - Lançamento, Disponibilidade geral. <- você está aqui
O que temos dessa programação? Sim, de fato, nada - acabamos de chegar à linha de chegada e assistimos aos amantes do legado de uma altura do novíssimo JDK 12.
Bugs! Panic! Tudo para o fundo!

Quando uma nova versão não LTS é lançada, geralmente todos não dão a mínima para novos recursos. É mais interessante se tudo desmoronar para o inferno.
Claro, existem muitos bugs, mas não no JDK 12 :) A julgar pelo jir, tudo é normal:

Vou citar a solicitação para que você entenda exatamente qual é a "norma":
project = JDK AND issuetype = Bug AND status in (Open, "In Progress", New) AND priority in (P1) AND (fixVersion in (12) OR fixVersion is EMPTY AND affectedVersion in (12) AND affectedVersion not in regexVersion("11.*", "10.*", "9.*", "8.*", "7.*", "6.*")) AND (labels is EMPTY OR labels not in (jdk12-defer-request, noreg-demo, noreg-doc, noreg-self)) AND (component not in (docs, globalization, infrastructure) OR component = infrastructure AND subcomponent = build) AND reporter != "Shadow Bug" ORDER BY priority, component, subcomponent, assignee
Obviamente, em geral os bugs têm um lugar para estar, eles não vão a lugar algum em um projeto tão grande. Alega-se apenas que, no momento, os erros de P1 não foram notados.
Uma comunicação mais formal com bugs é declarada em um documento especial, JEP 3: JDK Release Process , que pertence ao nosso administrador imortal nas ondas turbulentas do oceano Java - Mark Reinhold.
E, em particular, vale a pena desenterrar um parágrafo dizendo quem é o culpado e o que fazer como transferir ingressos se você não tiver tempo para o 12º lançamento. É necessário colocar no rastreador de erros o rótulo jdk$N-defer-request
no qual N indica de qual release você deseja transferir e deixar um comentário cuja primeira linha seja Pedido de Adiamento . Além disso, a análise de todas essas solicitações é feita pelos líderes das respectivas áreas e projetos.
Os problemas de aprovação do TCK não podem ser ignorados dessa maneira - é garantido que o Java continua sendo o Java, e não algo parecido com um sapo. O jdk$N-defer-request label
nunca desaparece. É interessante o que eles fazem com pessoas que violam a regra de não excluir tags - sugiro alimentar porquinhos da índia.
No entanto, desta maneira, você pode ver quantos bugs foram portados para o JDK 13. Vamos tentar esta consulta:
project = JDK AND issuetype = Bug AND status in (Open, "In Progress", New) AND (labels in (jdk12-defer-request) AND labels not in (noreg-demo, noreg-doc, noreg-self)) AND (component not in (docs, globalization, infrastructure) OR component = infrastructure AND subcomponent = build) AND reporter != "Shadow Bug" ORDER BY priority, component, subcomponent, assignee
Apenas uma peça, JDK-8216039 : "TLS com BC e RSASSA-PSS quebra o ECDHServerKeyExchange". Não é grosso. Se esse argumento ainda não ajudar, então, como seu advogado, recomendo tentar um sedativo.
E qual é o resultado final?

É claro que a maioria dos recursos não afeta os usuários (programadores Java), mas os desenvolvedores do próprio OpenJDK. Portanto, apenas no caso, divido os recursos em externo e interno . Você pode pular os internos, mas estou ofendido, escrevi muito texto.
189: Shenandoah: um coletor de lixo com pouco tempo de pausa (experimental)
Recurso externo . Em resumo, as pessoas não gostam quando o Java fica mais lento, principalmente se o SLA exigir capacidade de resposta da ordem de 10 a 500 milissegundos. Agora, temos um GC de baixo impacto gratuito que está tentando trabalhar mais perto da borda esquerda desse intervalo. A desvantagem é que trocamos a CPU e a RAM para reduzir a latência. As fases de marcação e compactação de quadril funcionam em paralelo com os threads de aplicativos ativos. As pequenas pausas restantes devem-se ao fato de você ainda precisar pesquisar e atualizar as raízes do gráfico de objetos.
Se nenhuma das opções acima faz sentido para você - não importa, Shenandoah simplesmente funciona , independentemente de entender ou não os processos subjacentes.
Alexei Shipilev, Christina Flood e Roman Kennke estão trabalhando nisso - você precisa se esforçar para não conhecer essas pessoas. Se você geralmente entende como o GC funciona, mas não sabe o que um desenvolvedor pode fazer por lá, recomendo que você dê uma olhada na maravilhosa tradução do maravilhoso artigo de Leshina "Coletor de lixo caseiro para OpenJDK" ou da série JVM Anatomy Quarks . Isso é muito interessante .
Extremamente importante. A Oracle decidiu não enviar o Sheandoah com nenhuma de suas versões de lançamento - nem a do jdk.java.net nem a do oracle.com. Dado que Shenandoah é um dos recursos mais importantes do JDK 12, vale a pena instalar outra montagem oficial, por exemplo, da Azul .
230: Suíte Microbenchmark
Recurso interno . Se você já tentou escrever marcas de micropigmentação, sabe que isso é feito no JMH. O JMH é uma estrutura para criar, montar, ativar e analisar marcas de microbench para Java e outras linguagens da JVM. Você entende quem o escreveu (todas as coincidências são aleatórias). Infelizmente, nem tudo o que é feito no mundo dos aplicativos "normais" pode ser aplicado dentro do JDK. Por exemplo, é improvável que você veja o código normal do Spring Framework lá.
Felizmente, a partir da versão 12, é possível usar pelo menos JMH, e já existe um conjunto de testes gravados nela. Você pode vê-lo em jdk/jdk/test/micro/org/openjdk/bench
(você pode olhar diretamente no navegador, esse caminho é um link).
Por exemplo, eis a aparência do teste de GC .
Deixe-me lembrá-lo de que não temos o StackOverflow aqui e é proibido usar o código de copiar e colar, aqui e a seguir, sem ler e observar todas as licenças do arquivo correspondente e do projeto OpenJDK em geral, caso contrário, você poderá obter facilmente as últimas meias.
@BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) public class Alloc { public static final int LENGTH = 400; public static final int ARR_LEN = 100; public int largeLen = 100; public int smalllen = 6; @Benchmark public void testLargeConstArray(Blackhole bh) throws Exception { int localArrlen = ARR_LEN; for (int i = 0; i < LENGTH; i++) { Object[] tmp = new Object[localArrlen]; bh.consume(tmp); } }
325: Alternar expressões (visualização)
Recurso externo . Isso mudará fundamentalmente sua abordagem para escrever switches sem fim com mais de duas telas. Veja:
Virgem Java Switch vs ...
int dayNum = -1; switch (day) { case MONDAY: case FRIDAY: case SUNDAY: dayNum = 6; break; case TUESDAY: dayNum = 7; break; case THURSDAY: case SATURDAY: dayNum = 8; break; case WEDNESDAY: dayNum = 9; break; }
Por que é ruim : há muitas cartas, você pode pular o intervalo (especialmente se você é viciado em drogas ou está com TDAH).
... vs Chad Java Swtich Expression!
int dayNum = switch (day) { case MONDAY -> 0; case TUESDAY -> 1; default -> { int k = day.toString().length(); int result = f(k); break result; } };
Por que bom : poucas letras, seguro, conveniente, novo recurso interessante.
Bônus : se você é um sádico, isso lhe dará a maior satisfação, já que milhares de desenvolvedores de IDE agora são atormentados com o suporte desse recurso. Sim lany , sim? Você pode pegá-lo após o relatório em 6 de abril e gentilmente pedir para fornecer todos os detalhes sujos.
Este é um recurso de visualização, mas não funciona! Ao compilar, no javac
você precisa passar as opções da linha de comando --enable-preview --release 12
e executar o java
- apenas o sinalizador --enable-preview
.
334: API de constantes da JVM
Recurso interno . Os desenvolvedores querem manipular arquivos de classe. Você precisa fazer isso de forma conveniente, e esta é a declaração do problema. Pelo menos, é o que Brian Goetz, dono deste JEP, disse :-) Tudo isso faz parte de um campo de batalha maior, mas por enquanto não vamos nos aprofundar.
Cada classe Java possui um chamado "pool constante", no qual existe um dump de alguns valores (como strings e ints) ou entidades de tempo de execução, como classes e métodos. Você pode cavar esse despejo usando a instrução ldc - "load costant", para que todo esse lixo seja chamado de constantes carregáveis. Ainda existe um caso especial para invokedynamic, mas não importa.
Se trabalharmos com arquivos de classe, queremos simular convenientemente instrumentos de bytecode e, portanto, constantes carregáveis. O primeiro desejo é simplesmente criar os tipos Java correspondentes, mas como você os apresenta com uma classe "viva", a estrutura CONSTANT_Class_info
? Objetos de Class
dependem da correção e consistência do carregamento de classes e, com o carregamento de classes em Java, uma bacanal infernal é criada. Para começar, nem todas as classes podem ser carregadas na VM, mas você ainda precisa descrevê-las!
Eu gostaria de, de alguma forma, gerenciar coisas como classes, métodos e bestas menos conhecidas, como identificadores de métodos e constantes dinâmicas, levando em conta todas essas sutilezas.
Isso é resolvido com a introdução de novos tipos de links simbólicos baseados em valor (no sentido da JVMS 5.1 ), cada um dos quais descreve um tipo específico de constante. Descreve puramente nominalmente, isoladamente do carregamento de classes ou problemas de acesso. Eles vivem em pacotes como java.lang.invoke.constant
e não solicitam, mas você pode dar uma olhada no patch aqui .
340: Uma porta AArch64, não duas
Recurso externo . Já no JDK 9, havia uma situação estranha quando a Oracle e a Red Hat colocavam simultaneamente suas portas ARM em alerta. E agora vemos o final da história: a parte de 64 bits do porto de Oraklov foi removida do rio acima.
Você poderia ter mergulhado na história por um longo tempo, mas existe uma maneira melhor. A BellSoft participou do desenvolvimento deste JEP, e seu escritório está localizado em São Petersburgo, próximo ao antigo escritório da Oracle.
Portanto, imediatamente me virei imediatamente para Alexey Voitilov, CTO da BellSoft:
"A BellSoft lança o Liberica JDK, que, além do x86 Linux / Windows / Mac e Solaris / SPARC, também suporta ARM. A partir do JDK 9 para ARM, focamos em melhorar o desempenho da porta AARCH64 para aplicativos de servidor e continuamos a suportar a porta ARM de 32 bits para Portanto, na época do lançamento do JDK 11, havia uma situação em que ninguém suportava a parte da porta de 64 bits da Oracle (incluindo a Oracle), e a comunidade OpenJDK decidiu removê-la para se concentrar na porta AARCH64. No momento, é mais produtivo ( ver, por exemplo, PEC 315 , que integrado ao JDK 11) e, a partir do JDK 12, suporta todos os recursos presentes na porta do Oracle (o último VM mínimo que integrei em setembro). Portanto, fiquei feliz em ajudar Bob Vandette a remover esse rudimento no JDK 12. Como resultado, o OpenJDK a comunidade recebeu uma porta no AARCH64 e uma porta ARM32, o que certamente facilita o suporte ".
341: Arquivos CDS padrão
Recurso interno . O problema é que, no início de um aplicativo Java, milhares de classes são carregadas, o que cria a sensação de que o Java diminui significativamente na inicialização. Mas quem está lá para mentir, isso não é apenas uma "sensação" - é assim. Para resolver o problema desde os tempos antigos, vários rituais são praticados.
O Compartilhamento de dados de classe é um recurso que surgiu há séculos , como um recurso comercial do JDK 8, atualização 40. Ele permite que você empacote todo esse lixo de inicialização em um arquivo de algum formato próprio (você não precisa saber qual), após o qual a velocidade de inicialização aplicações está aumentando. Depois de um tempo, o JEP 310 apareceu: Application Class-Data Sharing, o que nos permitiu trabalhar da mesma maneira não apenas com as classes do sistema, mas também com as classes de aplicativos.
Para as classes JDK, é assim. Primeiro, despejamos as classes com o comando java -Xshare:dump
e, em seguida, executamos o aplicativo, solicitando que ele use esse cache: java -Xshare:on -jar app.jar
. Tudo, a startup melhorou um pouco. Você sabia sobre esse recurso? Muitos que ainda não sabem!
Parece estranho aqui: por que cada vez que escrever ritualmente -Xshare:dump
se o resultado padrão deste comando é um pouco previsível, mesmo no estágio de criação da distribuição JDK? De acordo com a documentação , se a distribuição Java 8 foi instalada usando o instalador, no momento da instalação, ela deve executar os comandos necessários para você. Como, o instalador está minando silenciosamente no canto. Mas porque? E o que fazer com a distribuição, que é distribuída não como instalador, mas como um arquivo zip?
É simples: a partir do JDK 12, o arquivo CDS será gerado pelos criadores do kit de distribuição, imediatamente após a vinculação. Mesmo para compilações noturnas (desde que sejam de 64 bits e nativas, não para compilação cruzada).
Os usuários nem precisam saber sobre a presença desse recurso, porque, começando com o JDK 11, -Xshare:auto
ativado por padrão, e esse arquivo é recuperado automaticamente. Assim, o simples fato de atualizar para o JDK 12 acelera o lançamento do aplicativo!
344: Coleções Mistas Abortáveis para G1
Recurso interno . Para ser honesto Eu não entendo nada no trabalho do G1 Uma explicação dos recursos do GC é uma tarefa ingrata. Requer uma compreensão dos detalhes de seu trabalho, tanto do explicador quanto do entendimento. Para a maioria das pessoas, o GC é uma espécie de inferno fora de uma caixa de rapé que você pode trapacear em caso de algo. Portanto, o problema deve ser explicado de alguma forma mais simples.
Problema : G1 pode funcionar melhor.
Bem, o problema é que o GC é um compromisso de muitos parâmetros, um dos quais é a duração da pausa. Às vezes, a pausa é muito longa e é bom poder cancelá-la.
Quando isso acontece? O G1 realmente analisa o comportamento do aplicativo e seleciona a frente do trabalho (expressa como um conjunto de coleta ) com base em suas conclusões. Quando o escopo do trabalho é aprovado, G1 se compromete a coletar todos os objetos vivos no conjunto de coleta, teimosamente e sem parar, de uma só vez. Às vezes, leva muito tempo. Em essência, isso significa que G1 calculou incorretamente a quantidade de trabalho. Você pode enganá-lo, alterando repentinamente o comportamento do seu aplicativo para que a heurística funcione com dados incorretos quando muitas regiões antigas entrarem no conjunto de coleta.
Para sair da situação, o G1 foi finalizado pelo seguinte mecanismo: se a heurística seleciona regularmente a quantidade errada de trabalho, o G1 muda para a coleta incremental de lixo, passo a passo, e cada próximo passo (se não se encaixar no tempo de execução do destino) pode ser cancelado. Não faz sentido coletar algo de forma incremental (regiões jovens), portanto, todo esse trabalho é destacado no bloco “obrigatório”, que ainda é realizado continuamente.
O que fazer com o usuário final? Nada, você precisa atualizar para o JDK 12, tudo ficará melhor por si só.
346: Retorno imediato da memória confirmada não utilizada do G1
Recurso interno . O problema é que, se temos um grande quadril que ninguém usa ativamente, parece justo recuperar toda essa memória inativa de volta ao sistema operacional. Antes do JDK 12, no entanto, isso não acontecia.
Para atingir seu objetivo em termos do comprimento de pausa permitido, o G1 executa um conjunto de ciclos incrementais, paralelos e de vários estágios. No JDK 11, ele fornece memória confirmada ao sistema operacional apenas com GC completo ou durante a fase de marcação paralela. Se você conectar o log (-Xloggc: /home/gc.log -XX: + PrintGCDetails -XX: + PrintGCDateStamps), essa fase será exibida da seguinte forma:
8801.974: [G1Ergonomics (Concurrent Cycles) request concurrent cycle initiation, reason: occupancy higher than threshold, occupancy: 12582912000 bytes, allocation request: 0 bytes, threshold: 12562779330 bytes (45.00 %), source: end of GC] 8804.670: [G1Ergonomics (Concurrent Cycles) initiate concurrent cycle, reason: concurrent cycle initiation requested] 8805.612: [GC concurrent-mark-start] 8820.483: [GC concurrent-mark-end, 14.8711620 secs]
O engraçado é que o G1, como pode, luta com paradas completas, e o ciclo simultâneo começa apenas com alocações frequentes e um monte entupido. Nossa situação, quando ninguém toca no quadril, é exatamente o oposto. Situações em que o G1 arranha para dar memória ao sistema operacional acontecem muito raramente!
Portanto, todos teriam pontuado esse problema ("compre ainda mais memória RAM, que é como um trapaceiro!"). Se não um, mas - existem todos os tipos de nuvens e contêineres nos quais isso significa utilização insuficiente e perda de dinheiro sério. Olha, que relatório legal , cheio de dor.
A solução foi ensinar G1 a se comportar bem nesse caso específico, como Shenanda ou GenCon do OpenJ9 já sabem. É necessário determinar a utilização insuficiente do quadril e, consequentemente, reduzir seu uso. Em alguns testes no Tomcat, isso permitiu reduzir o consumo de memória em quase metade.
A conclusão é que o aplicativo é considerado inativo ou se o intervalo (em milissegundos) getloadavg()
da última compilação e não há ciclo simultâneo ou se getloadavg()
por um período de um minuto mostrou uma carga abaixo de um determinado limite. Assim que isso acontece, a coleta periódica de lixo é iniciada - ela certamente não limpa, nem a montagem completa, mas afeta o aplicativo minimamente.
Você pode enviá-lo para este log:
(1) [6.084s][debug][gc,periodic ] Checking for periodic GC. [6.086s][info ][gc ] GC(13) Pause Young (Concurrent Start) (G1 Periodic Collection) 37M->36M(78M) 1.786ms (2) [9.087s][debug][gc,periodic ] Checking for periodic GC. [9.088s][info ][gc ] GC(15) Pause Young (Prepare Mixed) (G1 Periodic Collection) 9M->9M(32M) 0.722ms (3) [12.089s][debug][gc,periodic ] Checking for periodic GC. [12.091s][info ][gc ] GC(16) Pause Young (Mixed) (G1 Periodic Collection) 9M->5M(32M) 1.776ms (4) [15.092s][debug][gc,periodic ] Checking for periodic GC. [15.097s][info ][gc ] GC(17) Pause Young (Mixed) (G1 Periodic Collection) 5M->1M(32M) 4.142ms (5) [18.098s][debug][gc,periodic ] Checking for periodic GC. [18.100s][info ][gc ] GC(18) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 1.685ms (6) [21.101s][debug][gc,periodic ] Checking for periodic GC. [21.102s][info ][gc ] GC(20) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.868ms (7) [24.104s][debug][gc,periodic ] Checking for periodic GC. [24.104s][info ][gc ] GC(22) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.778ms
Descobriu? Eu não No JEP, há uma tradução detalhada em linguagem de sinais de cada linha do log, e como o algoritmo funciona, e tudo mais.
"Então o que, por que eu descobri?" - você pergunta. Agora, temos dois identificadores adicionais: G1PeriodicGCInterval
e G1PeriodicGCSystemLoadThreshold
, que podem ser distorcidos quando ficam ruins. É certo que um dia será ruim, é Java, baby!
Sumário
Como resultado, temos um forte lançamento em nossas mãos - não uma revolução, mas uma evolução focada em melhorar o desempenho. Exatamente metade das melhorias estão relacionadas ao desempenho: três JEPs sobre GC e um sobre CDS, que prometem ativar sozinhos, precisam apenas atualizar para o JDK 12. Além disso, temos um recurso de idioma (switch expression), duas novas ferramentas para desenvolvedores de JDK ( Testes de API de constantes e JMH), e agora a comunidade pode se concentrar melhor em uma única porta de 64 bits no ARM.
Em geral, atualize para o JDK 12 agora e a Força esteja com você. Você precisará disso.
Minuto de publicidade. Muito em breve, de 5 a 6 de abril, será realizada a conferência JPoint, que reunirá um grande número de pessoas que sabem muito sobre o JDK e todos os tipos de novos recursos. Por exemplo, certamente haverá Simon Ritter, da Azul, com uma palestra sobre "JDK 12: Armadilhas para os incautos" . O lugar mais apropriado para discutir a versão mais recente! Você pode aprender mais sobre o JPoint no site oficial .