Por que o JVM não armazena em cache o código JIT compilado?

107

A implementação JVM canônica da Sun aplica uma otimização bastante sofisticada ao bytecode para obter velocidades de execução quase nativas depois que o código foi executado algumas vezes.

A questão é: por que esse código compilado não é armazenado em cache no disco para uso durante os usos subsequentes da mesma função / classe?

Do jeito que está, toda vez que um programa é executado, o compilador JIT é ativado novamente, em vez de usar uma versão pré-compilada do código. Adicionar esse recurso não aumentaria significativamente o tempo de execução inicial do programa, quando o bytecode está essencialmente sendo interpretado?

Chinmay Kanchi
fonte
4
Um tópico discutindo este problema: javalobby.org/forums/thread.jspa?threadID=15812
miku
2
Mas uma pergunta improvável para atrair uma resposta definitiva.
bmargulies
1
Não tenho certeza sobre um aumento "significativo", porque então você teria que carregar o material JITted do disco em vez de fazer o JIT na memória. Isso poderia acelerar as coisas, mas caso a caso.
R. Martinho Fernandes
1
Obrigado pelas ótimas respostas a todos! Todas as respostas eram igualmente válidas, então eu fui com a comunidade nesta ...
Chinmay Kanchi
Esta é uma boa pergunta se você me perguntar :)
Alfred

Respostas:

25

Sem recorrer ao cut'n'paste do link que @MYYN postou, suspeito que seja porque as otimizações que a JVM realiza não são estáticas, mas sim dinâmicas, baseadas nos padrões de dados e também nos padrões de código. É provável que esses padrões de dados mudem durante a vida útil do aplicativo, tornando as otimizações em cache menos do que ideais.

Portanto, você precisaria de um mecanismo para estabelecer se as otimizações salvas ainda eram ótimas, ponto em que você também pode otimizar novamente em tempo real.

skaffman
fonte
6
... ou você poderia simplesmente oferecer persistência como uma opção , como faz a JVM da Oracle - capacite os programadores avançados a otimizar o desempenho de seus aplicativos quando e onde eles sabem que os padrões não estão mudando, sob sua responsabilidade. Por que não?!
Alex Martelli
2
Porque provavelmente não vale a pena. Se nem a SUN, nem a IBM nem a BEA consideraram que vale a pena por suas JVMs de desempenho, haverá um bom motivo para isso. Talvez sua otimização RT seja mais rápida do que a do Oracle, e é por isso que a Oracle o armazena em cache.
Skaffman
9
Por que não tomar as otimizações armazenadas como ponto de partida, para usar o que foi aprendido em execuções anteriores? A partir daí, o JIT poderia funcionar normalmente e otimizar as coisas. No desligamento, esse código pode ser persistido novamente e usado na próxima execução como um novo ponto de partida.
Puce
1
@Puce A única razão que posso pensar é que AFAIK você não obtém estatísticas de perfil ao executar código otimizado. Então você não teria como melhorar ...
maaartinus
1
Eu pessoalmente ficaria bem com a opção "apenas persistir as informações de perfil JIT entre as execuções" com todos os avisos de que "isso só será válido com exatamente a mesma JVM, os mesmos dados etc. e, de outra forma, ignorado". Em relação ao motivo pelo qual isso não foi implementado, eu esperaria que a complexidade adicional de persistir e validar os dados iniciais JIT fosse demais para tirar recursos de outros projetos. Dada a escolha entre este e os fluxos lambda + do Java 8, prefiro o último.
Thorbjørn Ravn Andersen
25

A JVM da Oracle está realmente documentada para fazer isso - citando a Oracle,

o compilador pode tirar proveito do modelo de resolução de classe do Oracle JVM para opcionalmente persistir métodos Java compilados em chamadas de banco de dados, sessões ou instâncias. Essa persistência evita a sobrecarga de recompilações desnecessárias entre sessões ou instâncias, quando se sabe que semanticamente o código Java não mudou.

Não sei por que todas as implementações sofisticadas de VM não oferecem opções semelhantes.

Alex Martelli
fonte
8
Porque outras JVMs sofisticadas não têm um grande RDBMS empresarial acessível para armazenar coisas em :)
skaffman
Uau! isso significa que às vezes as compilações são armazenadas em cache. Esta é uma boa notícia!
Sandeep Jindal
O J9 da IBM também está documentado para fazer isso.
user314104
9
Observe que este Oracle JVM está dentro do banco de dados Oracle, não o download que a Oracle obteve ao adquirir a Sun.
Thorbjørn Ravn Andersen
14

Uma atualização para as respostas existentes - Java 8 tem um JEP dedicado a resolver isso:

=> JEP 145: Código Compilado de Cache . Novo link .

Em um nível muito alto, seu objetivo declarado é :

Salve e reutilize código nativo compilado de execuções anteriores para melhorar o tempo de inicialização de grandes aplicativos Java.

Espero que isto ajude.

Eugen
fonte
3
O recurso não chegou à versão final .
assylias
5

Excelsior JET tem um compilador JIT de cache desde a versão 2.0, lançada em 2001. Além disso, seu compilador AOT pode recompilar o cache em um único objeto DLL / compartilhado usando todas as otimizações.

Dmitry Leskov
fonte
3
Sim, mas a pergunta era sobre a JVM canônica, ou seja, a JVM da Sun. Estou bem ciente de que existem vários compiladores AOT para Java, bem como outras JVMs de cache.
Chinmay Kanchi
0

Não sei as reais razões, não estando de forma alguma envolvido na implementação da JVM, mas posso pensar em algumas plausíveis:

  • A ideia do Java é ser uma linguagem de escrita uma vez executado em qualquer lugar, e colocar coisas pré-compiladas no arquivo de classe é meio que violar isso (apenas "mais ou menos" porque é claro que o código de bytes real ainda estaria lá)
  • Isso aumentaria os tamanhos dos arquivos de classe porque você teria o mesmo código lá várias vezes, especialmente se acontecer de você executar o mesmo programa em várias JVMs diferentes (o que não é realmente incomum, quando você considera versões diferentes como JVMs diferentes, o que você realmente tem que fazer)
  • Os arquivos de classe em si podem não ser graváveis ​​(embora seja muito fácil verificar isso)
  • As otimizações JVM são parcialmente baseadas em informações de tempo de execução e em outras execuções podem não ser tão aplicáveis ​​(embora ainda devam fornecer algum benefício)

Mas eu realmente estou supondo, e como você pode ver, eu realmente não acho que nenhuma das minhas razões seja um obstáculo. Eu acho que a Sun simplesmente não considera esse suporte como uma prioridade, e talvez meu primeiro motivo esteja próximo da verdade, já que fazer isso normalmente também pode levar as pessoas a pensar que os arquivos de classe Java realmente precisam de uma versão separada para cada VM em vez de ser plataforma cruzada.

Minha forma preferida seria ter um tradutor separado de bytecode para nativo que você pudesse usar para fazer algo assim explicitamente de antemão, criando arquivos de classe que são explicitamente construídos para uma VM específica, possivelmente com o bytecode original neles para que você pode ser executado com diferentes VMs também. Mas isso provavelmente vem da minha experiência: tenho feito principalmente Java ME, onde realmente dói que o compilador Java não seja mais inteligente sobre compilação.

JaakkoK
fonte
1
há um ponto no arquivo de classe para essas coisas, de fato, essa era a intenção original (armazene o código JIT como um atributo no arquivo de classe).
TofuBeer
@TofuBeer: Obrigado pela confirmação. Suspeitei que fosse esse o caso (é o que eu teria feito), mas não tinha certeza. Editado para remover isso como um possível motivo.
JaakkoK
Eu acho que você acertou o prego na cabeça com sua última bala. As outras poderiam ser contornadas, mas essa última parte é, eu acho, o principal motivo pelo qual o código JITed não é persistido.
Sasha Chedygov
1
O último parágrafo sobre o compilador de bytecode para nativo explícito é o que você tem atualmente em .NET com NGEN ( msdn.microsoft.com/en-us/library/6t9t5wcf(VS.71).aspx ).
R. Martinho Fernandes