Máquina virtual e CLR do Java

140

Como uma espécie de acompanhamento da pergunta chamada Diferenças entre o MSIL e o bytecode Java? , quais são as (principais) diferenças ou semelhança em como a Java Virtual Machine funciona versus como o.NET Framework O Common Language Runtime (CLR) funciona?

Além disso, é o Estrutura .NET CLR uma "máquina virtual" ou não possui os atributos de uma máquina virtual?

Frank V
fonte
Bem, se você comparar comparações semelhantes, deverá reformular a pergunta como a diferença entre a VM e o CLR (Common Language Runtime), que é o análogo direto da VM.
Cletus

Respostas:

278

Existem muitas semelhanças entre as duas implementações (e na minha opinião: sim, elas são "máquinas virtuais").

Por um lado, ambas são VMs baseadas em pilha, sem noção de "registradores", como estamos acostumados a ver em uma CPU moderna como o x86 ou o PowerPC. A avaliação de todas as expressões ((1 + 1) / 2) é realizada empurrando operandos para a "pilha" e, em seguida, removendo-os da pilha sempre que uma instrução (adicionar, dividir, etc.) precisar consumir esses operandos. Cada instrução envia seus resultados de volta para a pilha.

É uma maneira conveniente de implementar uma máquina virtual, porque praticamente todas as CPUs do mundo têm uma pilha, mas o número de registros geralmente é diferente (e alguns registros são para fins especiais, e cada instrução espera seus operandos em registros diferentes, etc.) )

Portanto, se você for modelar uma máquina abstrata, um modelo puramente baseado em pilha é um bom caminho a percorrer.

Obviamente, máquinas reais não funcionam dessa maneira. Portanto, o compilador JIT é responsável por executar o "registro" das operações do bytecode, essencialmente planejando os registros reais da CPU para conter operandos e resultados sempre que possível.

Então, acho que esse é um dos maiores pontos em comum entre o CLR e a JVM.

Quanto às diferenças ...

Uma diferença interessante entre as duas implementações é que o CLR inclui instruções para criar tipos genéricos e, em seguida, para aplicar especializações paramétricas a esses tipos. Portanto, no tempo de execução, o CLR considera uma Lista <int> como um tipo completamente diferente de uma Lista <>.

Nos bastidores, ele usa o mesmo MSIL para todas as especializações de tipo de referência (para que uma Lista <> use a mesma implementação que uma Lista <Objeto>, com diferentes tipos de conversão nos limites da API), mas cada tipo de valor usa sua própria implementação exclusiva (Lista <int> gera código completamente diferente da Lista <double>).

Em Java, tipos genéricos são apenas um truque de compilador. A JVM não tem noção de quais classes possuem argumentos de tipo e é incapaz de executar especializações paramétricas em tempo de execução.

De uma perspectiva prática, isso significa que você não pode sobrecarregar métodos Java em tipos genéricos. Você não pode ter dois métodos diferentes, com o mesmo nome, diferindo apenas se eles aceitam uma Lista <> ou uma Lista <Data>. Obviamente, como o CLR conhece os tipos paramétricos, não há problemas em lidar com métodos sobrecarregados em especializações de tipos genéricos.

No dia a dia, essa é a diferença que mais noto entre o CLR e a JVM.

Outras diferenças importantes incluem:

  • O CLR possui encerramentos (implementados como representantes de C #). A JVM suporta fechamentos apenas desde o Java 8.

  • O CLR possui corotinas (implementadas com a palavra-chave C # 'yield'). A JVM não.

  • O CLR permite que o código do usuário defina novos tipos de valor (structs), enquanto a JVM fornece uma coleção fixa de tipos de valor (byte, short, int, long, float, double, char, boolean) e permite apenas que os usuários definam novas referências- tipos (classes).

  • O CLR fornece suporte para declarar e manipular ponteiros. Isso é especialmente interessante porque a JVM e o CLR empregam implementações estritas de coletor de lixo de compactação geracional como estratégia de gerenciamento de memória. Em circunstâncias normais, um GC de compactação rigoroso tem muita dificuldade com ponteiros, porque quando você move um valor de um local de memória para outro, todos os ponteiros (e ponteiros para ponteiros) se tornam inválidos. Mas o CLR fornece um mecanismo de "fixação" para que os desenvolvedores possam declarar um bloco de código dentro do qual o CLR não tem permissão para mover determinados ponteiros. É muito conveniente.

  • A maior unidade de código na JVM é um 'pacote', como evidenciado pela palavra-chave 'protected', ou possivelmente um JAR (ou seja, Java ARchive), como evidenciado por ser capaz de especificar um jar no caminho de classe e tratá-lo como uma pasta de código. No CLR, as classes são agregadas em 'assemblies', e o CLR fornece lógica para raciocinar e manipular assemblies (carregados em "AppDomains", fornecendo caixas de proteção no nível de sub-aplicativo para alocação de memória e execução de código).

  • O formato de bytecode CLR (composto de instruções e metadados do MSIL) possui menos tipos de instruções que a JVM. Na JVM, toda operação exclusiva (adicione dois valores int, adicione dois valores flutuantes, etc.) possui sua própria instrução exclusiva. No CLR, todas as instruções MSIL são polimórficas (adicione dois valores) e o compilador JIT é responsável por determinar os tipos dos operandos e criar o código de máquina apropriado. Mas não sei qual é a estratégia preferida. Ambos têm trocas. O compilador HotSpot JIT, para a JVM, pode usar um mecanismo de geração de código mais simples (não precisa determinar os tipos de operandos, porque eles já estão codificados na instrução), mas isso significa que precisa de um formato de bytecode mais complexo, com mais tipos de instruções.

Uso Java (e admiro a JVM) há cerca de dez anos.

Mas, na minha opinião, o CLR é agora a implementação superior, em quase todos os aspectos.

benjismith
fonte
73
Encerramentos e geradores são implementados no nível do idioma e são simplesmente representados como classes no nível CLR.
27290 Curt Hagenlocher
2
E as diferenças em como eles lidam com a pilha? O CLR é mais dependente do processo do SO / host, enquanto a JVM gerencia a memória heap mais ou menos completamente.
Kelly S. French
6
Uma diferença importante é o contraste entre a compilação just-in-time (CLR) e a otimização adaptativa na JVM (Oracle / Sun).
Edwin Dalorzo
1
Os slots variáveis ​​locais do Java agem como registros. Mas é tudo discutível de qualquer maneira, já que o JIT transforma slots locais e a pilha em registros reais.
Antimony 02/03
1
@kuhajeyan, porque quando o CLR foi introduzido, a JVM tinha 10 anos. isso é muito tempo em TI. Quando a JVM chegou em 1993, não havia um candidato sério; para CLR (2003), havia uma JVM madura e sólida, com forte presença na indústria.
Companheiro simples
25

Sua primeira pergunta é comparar a JVM com o .NET Framework - suponho que você realmente quis comparar com o CLR. Se sim, acho que você poderia escrever um pequeno livro sobre isso ( EDIT: parece que Benji já tem :-)

Uma diferença importante é que o CLR foi projetado para ser uma arquitetura neutra em linguagem, diferente da JVM.

Outra diferença importante é que o CLR foi projetado especificamente para permitir um alto nível de interoperabilidade com o código nativo. Isso significa que o CLR deve gerenciar a confiabilidade e a segurança quando a memória nativa é acessada e modificada, além de gerenciar o empacotamento entre estruturas de dados baseadas em CLR e estruturas de dados nativas.

Para responder à sua segunda pergunta, o termo “máquina virtual” é um termo mais antigo do mundo do hardware (por exemplo, virtualização da IBM da 360 na década de 1960) que costumava significar uma emulação de software / hardware da máquina subjacente para realizar o mesmo tipo de coisas que o VMWare faz.

O CLR é frequentemente chamado de "mecanismo de execução". Nesse contexto, é uma implementação de uma máquina de IL em cima de um x86. Isso também é o que a JVM faz, embora você possa argumentar que há uma diferença importante entre os bytecodes polimórficos do CLR e os bytecodes digitados pela JVM.

Portanto, a resposta pedante à sua segunda pergunta é "não". Mas tudo se resume a como você define esses dois termos.

EDIT: Mais uma diferença entre a JVM e o CLR é que a JVM (versão 6) é muito relutante em liberar memória alocada de volta para o sistema operacional, mesmo onde pode.

Por exemplo, digamos que um processo da JVM seja iniciado e aloque 25 MB de memória do sistema operacional inicialmente. O código do aplicativo tenta alocações que exigem 50 MB adicionais. A JVM alocará 50 MB adicionais do sistema operacional. Depois que o código do aplicativo parar de usar essa memória, ele é coletado como lixo e o tamanho do heap da JVM diminui. No entanto, a JVM liberará apenas a memória alocada do sistema operacional sob determinadas condições circunstâncias muito específicas . Caso contrário, pelo restante da vida útil do processo, a memória permanecerá alocada.

O CLR, por outro lado, libera a memória alocada de volta para o sistema operacional, se não for mais necessário. No exemplo acima, o CLR teria liberado a memória assim que o heap tivesse diminuído.

HTTP 410
fonte
2
Não é absolutamente correto que a JVM não libere memória alocada. Veja a minha resposta a esta pergunta para a prova: stackoverflow.com/questions/366658/...
Michael Borgwardt
Vi a JVM retornar a memória de volta ao Windows.
27410 Steve Kuo
Alterei minha resposta para dizer que a JVM 6 é muito relutante em liberar memória, com links para as respostas de Ran e Michael. Eu nunca vi esse comportamento na JVM 5, então talvez essa versão tenha sido ainda mais relutante.
HTTP 410
Você poderia cobrir como a JVM gerencia ativamente o heap enquanto o CLR depende do processo pai? O exemplo específico que eu uso é que a JVM possui argumentos de tempo de execução para o tamanho máximo de heap, enquanto o ambiente CLR padrão não. Embora seja verdade que um aplicativo CLR hospedado no IIS possa configurar o IIS para limitar a memória, isso significaria incluir o IIS na definição de máquina virtual.
Kelly S. Francês
@ Steve Kuo, sim, eu também vi isso. geralmente entre as 17:00 e as 18:00.
Companheiro Simples
11

O CLR e a JVM são máquinas virtuais.

O .NET Framework e o Java Runtime Environment são o pacote das respectivas VMs e suas bibliotecas. Sem bibliotecas, as VMs são bastante inúteis.

Allain Lalonde
fonte
11

Mais detalhes sobre as diferenças podem ser encontrados em várias fontes acadêmicas e privadas. Um bom exemplo é o CLR Design Choices .

Alguns exemplos específicos incluem:

  • Alguns opperandos de baixo nível são digitados como "add two ints", onde o CLR usa um operando polimórfico. (ou seja, fadd / iadd / ladd vs apenas adicionar)
  • Atualmente, a JVM realiza perfis e otimização de tempo de execução mais agressivos (ou seja, Hotspot). Atualmente, o CLR faz otimizações de JIT, mas não otimização de tempo de execução (ou seja, substitui o código enquanto você estiver executando).
  • O CLR não alinha métodos virtuais, a JVM ...
  • Suporte para tipos de valor no CLR além dos "primitivos".
James Schek
fonte
-11

Não é uma máquina virtual, a estrutura .net compila os assemblies no binário nativo no momento da primeira execução:

Na computação, a compilação just-in-time (JIT), também conhecida como tradução dinâmica, é uma técnica para melhorar o desempenho em tempo de execução de um programa de computador. O JIT se baseia em duas idéias anteriores em ambientes de tempo de execução: compilação de bytecode e compilação dinâmica. Ele converte o código em tempo de execução antes de executá-lo nativamente, por exemplo, bytecode em código de máquina nativo. A melhoria de desempenho em relação aos intérpretes se origina do armazenamento em cache dos resultados da tradução de blocos de código, e não apenas da reavaliação de cada linha ou operando cada vez que ele é atendido (consulte Idioma interpretado). Ele também possui vantagens em compilar estaticamente o código no momento do desenvolvimento, pois ele pode recompilar o código, se isso for vantajoso, e pode impor garantias de segurança.

Vários ambientes de tempo de execução modernos, como o .NET Framework da Microsoft, a maioria das implementações de Java e, mais recentemente, o Actionscript 3, contam com a compilação JIT para execução de código em alta velocidade.

Fonte: http://en.wikipedia.org/wiki/Just-in-time_compilation

A adição do .NET framework contém uma máquina virtual, assim como Java.

Diones
fonte
10
Só porque a máquina virtual utiliza o JIT para otimização de desempenho não significa que não é mais uma máquina virtual. Quando o programador compila ele compila para a máquina virtual, deixando-se à implementação de realizar a execução no entanto lhe aprouver
Allain Lalonde