Máquina Virtual JAVA - Java virtual machine

Máquina Virtual JAVA
Designer Sun Microsystems
Bits 32 bits
Introduzido 1994
Versão 15.0.3
Modelo Empilhar e registrar-registrar
Codificação Variável
Ramificação Compare e ramifique
Endianness Grande
Abrir sim
Registros
Propósito geral Pilha de operandos por método (até 65535 operandos) mais variáveis ​​locais por método (até 65535)
Visão geral de uma arquitetura de máquina virtual Java (JVM) baseada em The Java Virtual Machine Specification Java SE 7 Edition

Uma máquina virtual Java ( JVM ) é uma máquina virtual que permite que um computador execute programas Java , bem como programas escritos em outras linguagens que também são compilados para bytecode Java . A JVM é detalhada por uma especificação que descreve formalmente o que é necessário em uma implementação de JVM. Ter uma especificação garante a interoperabilidade dos programas Java em diferentes implementações para que os autores do programa que usam o Java Development Kit (JDK) não precisem se preocupar com idiossincrasias da plataforma de hardware subjacente.

A implementação de referência JVM é desenvolvida pelo projeto OpenJDK como código- fonte aberto e inclui um compilador JIT chamado HotSpot . As versões Java com suporte comercial disponíveis na Oracle Corporation são baseadas no tempo de execução OpenJDK. Eclipse OpenJ9 é outra JVM de código aberto para OpenJDK.

Especificação JVM

A máquina virtual Java é um computador abstrato (virtual) definido por uma especificação. É uma parte do Java Runtime Environment. O algoritmo de coleta de lixo usado e qualquer otimização interna das instruções da máquina virtual Java (sua tradução em código de máquina ) não são especificados. A principal razão para essa omissão é não restringir desnecessariamente os implementadores. Qualquer aplicativo Java pode ser executado apenas dentro de alguma implementação concreta da especificação abstrata da máquina virtual Java.

Começando com Java Platform, Standard Edition (J2SE) 5.0, as mudanças na especificação JVM foram desenvolvidas sob o Java Community Process como JSR 924. A partir de 2006, as mudanças na especificação para suportar mudanças propostas para o formato de arquivo de classe (JSR 202) são sendo feito como uma versão de manutenção do JSR 924. A especificação para o JVM foi publicada como o livro azul , o prefácio afirma:

Nós pretendemos que esta especificação deve suficientemente documentar o Java Virtual Machine para fazer possíveis implementações de sala limpa compatíveis. A Oracle fornece testes que verificam o bom funcionamento das implementações da Java Virtual Machine.

Uma das JVMs da Oracle é chamada de HotSpot, a outra, herdada da BEA Systems, é JRockit . Implementações de Java de sala limpa incluem Kaffe , OpenJ9 e CEE-J de Skelmir . A Oracle possui a marca comercial Java e pode permitir seu uso para certificar suítes de implementação como totalmente compatíveis com as especificações da Oracle.

Carregador de classes

Uma das unidades organizacionais do código de bytes JVM é uma classe . Uma implementação de carregador de classes deve ser capaz de reconhecer e carregar qualquer coisa que esteja em conformidade com o formato de arquivo de classe Java . Qualquer implementação é livre para reconhecer outras formas binárias além dos arquivos de classe , mas deve reconhecer os arquivos de classe .

O carregador de classes executa três atividades básicas nesta ordem estrita:

  1. Carregando: encontra e importa os dados binários para um tipo
  2. Vinculação: realiza verificação, preparação e (opcionalmente) resolução
    • Verificação: garante a correção do tipo importado
    • Preparação: aloca memória para variáveis ​​de classe e inicializa a memória para valores padrão
    • Resolução: transforma referências simbólicas do tipo em referências diretas.
  3. Inicialização: invoca o código Java que inicializa as variáveis ​​de classe com seus valores iniciais adequados.

Em geral, existem dois tipos de carregador de classes: carregador de classes bootstrap e carregador de classes definido pelo usuário.

Cada implementação de máquina virtual Java deve ter um carregador de classes de bootstrap, capaz de carregar classes confiáveis ​​e um carregador de classes de extensão ou carregador de classes de aplicativos. A especificação da máquina virtual Java não especifica como um carregador de classes deve localizar as classes.

Arquitetura de máquina virtual

A JVM opera em tipos específicos de dados conforme especificado nas especificações da Java Virtual Machine. Os tipos de dados podem ser divididos em tipos primitivos ( inteiros , ponto flutuante, longo etc.) e tipos de referência. O JVM anterior era apenas uma máquina de 32 bits . longe os doubletipos, que são de 64 bits , são suportados nativamente, mas consomem duas unidades de armazenamento nas variáveis ​​locais de um quadro ou pilha de operandos, uma vez que cada unidade tem 32 bits. boolean, byte, short, E chartipos são todos -sinal estendido (exceto charque é estendido com zeros ) e operado como inteiros de 32 bits, o mesmo que inttipos. Os tipos menores têm apenas algumas instruções específicas de tipo para carregamento, armazenamento e conversão de tipo. booleané operado como bytevalores de 8 bits , com 0 representando falsee 1 representando true. (Embora booleantenha sido tratado como um tipo desde o Java Virtual Machine Specification, segunda edição esclareceu esta questão, em compilado e executado código, há pouca diferença entre um booleane uma byteexceção de nome deturpação em assinaturas de método e o tipo de matrizes de booleanos. booleanS em as assinaturas de método são mutiladas Zenquanto bytes são mutiladas como B. Arrays booleanos carregam o tipo, boolean[]mas usam 8 bits por elemento, e a JVM não tem capacidade integrada para empacotar booleanos em uma matriz de bits , exceto pelo tipo que executam e se comportam mesmo que bytematrizes. Em todos os outros usos, o booleantipo é efetivamente desconhecido para a JVM, pois todas as instruções para operar em booleanos também são usadas para operar em bytes.) No entanto, as versões mais recentes da JVM (OpenJDK HotSpot JVM) suportam 64 bits, você pode ambos têm JVM de 32 bits / 64 bits em um sistema operacional de 64 bits. A principal vantagem de executar Java em um ambiente de 64 bits é o maior espaço de endereço. Isso permite um tamanho de heap Java muito maior e um número máximo aumentado de Java Threads, que é necessário para certos tipos de aplicativos grandes; no entanto, há uma queda de desempenho no uso de JVM de 64 bits em comparação com JVM de 32 bits.

A JVM possui um heap com coleta de lixo para armazenar objetos e matrizes. Código, constantes e outros dados de classe são armazenados na "área de método". A área do método é logicamente parte do heap, mas as implementações podem tratar a área do método separadamente do heap e, por exemplo, não podem coletá-la como lixo. Cada encadeamento JVM também tem sua própria pilha de chamadas (chamada de "pilha de máquina virtual Java" para maior clareza), que armazena quadros . Um novo quadro é criado cada vez que um método é chamado e o quadro é destruído quando esse método é encerrado.

Cada quadro fornece uma "pilha de operandos" e uma matriz de "variáveis ​​locais". A pilha de operandos é usada para operandos para cálculos e para receber o valor de retorno de um método chamado, enquanto variáveis ​​locais têm o mesmo propósito que registradores e também são usadas para passar argumentos de método. Portanto, a JVM é uma máquina de pilha e uma máquina de registro .

Instruções de bytecode

A JVM possui instruções para os seguintes grupos de tarefas:

O objetivo é a compatibilidade binária. Cada sistema operacional host específico precisa de sua própria implementação da JVM e do tempo de execução. Essas JVMs interpretam o bytecode semanticamente da mesma maneira, mas a implementação real pode ser diferente. Mais complexo do que apenas emular bytecode é implementar de forma compatível e eficiente a API principal do Java que deve ser mapeada para cada sistema operacional host.

Estas instruções operam em um conjunto de tipos de dados abstratos, em vez dos tipos de dados nativos de qualquer arquitetura de conjunto de instruções específico.

Linguagens JVM

Uma linguagem JVM é qualquer linguagem com funcionalidade que pode ser expressa em termos de um arquivo de classe válido que pode ser hospedado pela Java Virtual Machine. Um arquivo de classe contém instruções da Java Virtual Machine ( código de bytes Java ) e uma tabela de símbolos, bem como outras informações auxiliares. O formato de arquivo de classe é o formato binário independente de hardware e sistema operacional usado para representar classes e interfaces compiladas.

Existem várias linguagens JVM, tanto linguagens antigas portadas para JVM quanto linguagens completamente novas. JRuby e Jython são talvez as portas mais conhecidas de linguagens existentes, ou seja, Ruby e Python, respectivamente. Das novas linguagens que foram criadas do zero para compilar em bytecode Java, Clojure , Apache Groovy , Scala e Kotlin podem ser as mais populares. Um recurso notável com as linguagens JVM é que elas são compatíveis entre si , de forma que, por exemplo, as bibliotecas Scala podem ser usadas com programas Java e vice-versa.

Java 7 JVM implementa JSR 292: Suportando Linguagens de Tipagem Dinâmica na Plataforma Java, um novo recurso que oferece suporte a linguagens de Tipo Dinamicamente na JVM. Este recurso é desenvolvido dentro do projeto Da Vinci Machine , cuja missão é estender a JVM para que ela suporte outras linguagens além do Java.

Verificador de bytecode

Uma filosofia básica do Java é que é inerentemente seguro do ponto de vista de que nenhum programa do usuário pode travar a máquina host ou de outra forma interferir inadequadamente com outras operações na máquina host, e que é possível proteger certos métodos e estruturas de dados pertencentes a confiáveis código de acesso ou corrupção por código não confiável em execução na mesma JVM. Além disso, erros comuns do programador que geralmente levam à corrupção de dados ou comportamento imprevisível, como acessar o final de uma matriz ou usar um ponteiro não inicializado, não podem ocorrer. Vários recursos do Java se combinam para fornecer essa segurança, incluindo o modelo de classe, o heap com coleta de lixo e o verificador.

A JVM verifica todos os bytecodes antes de ser executado. Esta verificação consiste principalmente em três tipos de verificações:

  • As filiais são sempre para locais válidos
  • Os dados são sempre inicializados e as referências são sempre seguras para o tipo
  • O acesso a dados e métodos privados ou privados de pacotes é rigidamente controlado

As duas primeiras verificações ocorrem principalmente durante a etapa de verificação que ocorre quando uma classe é carregada e qualificada para uso. O terceiro é executado principalmente de forma dinâmica, quando os itens de dados ou métodos de uma classe são acessados ​​pela primeira vez por outra classe.

O verificador permite apenas algumas sequências de bytecode em programas válidos, por exemplo, uma instrução de salto (desvio) só pode ter como alvo uma instrução dentro do mesmo método . Além disso, o verificador garante que qualquer instrução opere em uma localização de pilha fixa, permitindo ao compilador JIT transformar acessos de pilha em acessos de registro fixos. Por causa disso, o fato de a JVM ser uma arquitetura de pilha não implica em uma penalidade de velocidade para a emulação em arquiteturas baseadas em registro ao usar um compilador JIT. Diante da arquitetura JVM verificada por código, não faz diferença para um compilador JIT se ele obtém registros imaginários nomeados ou posições de pilha imaginárias que devem ser alocadas aos registros da arquitetura de destino. Na verdade, a verificação de código torna a JVM diferente de uma arquitetura de pilha clássica, em que a emulação eficiente com um compilador JIT é mais complicada e normalmente realizada por um interpretador mais lento. Além disso, o intérprete usado pelo JVM padrão é um tipo especial conhecido como um intérprete de modelo, que traduz o bytecode diretamente para a linguagem de máquina nativa baseada em registro em vez de emular uma pilha como um intérprete típico (em muitos aspectos, o intérprete de HotSpot pode ser considerado um JIT Compiler ao invés de um verdadeiro intérprete), o que significa que a arquitetura de pilha que o bytecode almeja não é realmente usada na implementação, mas apenas uma especificação para a representação intermediária que pode ser implementada em uma arquitetura baseada em registro (outra instância de uma pilha arquitetura sendo meramente uma especificação e implementada em uma máquina virtual baseada em registro é o Common Language Runtime).

A especificação original para o verificador de bytecode usava linguagem natural que estava incompleta ou incorreta em alguns aspectos. Várias tentativas foram feitas para especificar a JVM como um sistema formal. Fazendo isso, a segurança das implementações JVM atuais pode ser analisada de forma mais completa e possíveis explorações de segurança evitadas. Também será possível otimizar a JVM pulando verificações de segurança desnecessárias, se o aplicativo em execução for comprovadamente seguro.

Execução segura de código remoto

Uma arquitetura de máquina virtual permite um controle muito refinado sobre as ações que o código dentro da máquina pode realizar. Ele assume que o código está "semanticamente" correto, ou seja, foi aprovado no processo verificador de bytecode (formal), materializado por uma ferramenta, possivelmente fora da máquina virtual. Isso é projetado para permitir a execução segura de código não confiável de fontes remotas, um modelo usado por miniaplicativos Java e outros downloads de código seguro. Uma vez verificado o bytecode, o código baixado é executado em uma " caixa de proteção " restrita , que é projetada para proteger o usuário de códigos maliciosos ou de comportamento incorreto. Como um complemento ao processo de verificação de bytecode, os editores podem comprar um certificado com o qual assinar digitalmente miniaplicativos como seguros, dando-lhes permissão para pedir ao usuário para sair da sandbox e acessar o sistema de arquivos local, área de transferência , executar peças externas de software , ou rede.

Provas formais de verificadores de bytecode foram feitas pelo setor de Javacard (Desenvolvimento formal de um verificador incorporado para código de bytes de Java Card)

Intérprete de bytecode e compilador just-in-time

Para cada arquitetura de hardware, é necessário um interpretador de bytecode Java diferente . Quando um computador possui um interpretador de bytecode Java, ele pode executar qualquer programa de bytecode Java, e o mesmo programa pode ser executado em qualquer computador que possua tal interpretador.

Quando o bytecode Java é executado por um interpretador, a execução sempre será mais lenta do que a execução do mesmo programa compilado na linguagem de máquina nativa. Este problema é atenuado por compiladores just-in-time (JIT) para executar bytecode Java. Um compilador JIT pode traduzir bytecode Java em linguagem de máquina nativa durante a execução do programa. As partes traduzidas do programa podem então ser executadas muito mais rapidamente do que poderiam ser interpretadas. Essa técnica é aplicada às partes de um programa freqüentemente executadas. Dessa forma, um compilador JIT pode acelerar significativamente o tempo de execução geral.

Não há conexão necessária entre a linguagem de programação Java e o bytecode Java. Um programa escrito em Java pode ser compilado diretamente na linguagem de máquina de um computador real e programas escritos em outras linguagens que não Java podem ser compilados em bytecode Java.

O bytecode Java se destina a ser independente de plataforma e seguro. Algumas implementações de JVM não incluem um interpretador, mas consistem apenas em um compilador just-in-time.

JVM no navegador da web

No início da vida útil da plataforma Java, a JVM foi comercializada como uma tecnologia da web para a criação de Rich Web Applications . A partir de 2018, a maioria dos navegadores da web e sistemas operacionais que empacotam navegadores da web não são fornecidos com um plug-in Java , nem permitem o side-loading de qualquer plug-in não Flash . O plug-in do navegador Java foi descontinuado no JDK 9.

O plug-in do navegador NPAPI Java foi projetado para permitir que a JVM execute os chamados miniaplicativos Java incorporados em páginas HTML. Para navegadores com o plug-in instalado, o miniaplicativo pode ser desenhado em uma região retangular na página atribuída a ele. Como o plug-in inclui uma JVM, os miniaplicativos Java não estão restritos à linguagem de programação Java; qualquer idioma destinado à JVM pode ser executado no plug-in. Um conjunto restrito de APIs permite que os miniaplicativos acessem o microfone do usuário ou a aceleração 3D, embora os miniaplicativos não sejam capazes de modificar a página fora de sua região retangular. Adobe Flash Player , a principal tecnologia concorrente, funciona da mesma forma neste aspecto.

Em junho de 2015, de acordo com a W3Techs, o uso de miniaplicativos Java e Silverlight caiu para 0,1% cada para todos os sites, enquanto o Flash caiu para 10,8%.

JVMs e interpretadores de JavaScript

Em maio de 2016, JavaPoly permite que os usuários importem bibliotecas Java não modificadas e as invoquem diretamente do JavaScript. JavaPoly permite que sites usem bibliotecas Java não modificadas, mesmo se o usuário não tiver o Java instalado em seu computador.

Compilação para JavaScript

Com as melhorias contínuas na velocidade de execução do JavaScript, combinadas com o aumento do uso de dispositivos móveis cujos navegadores da web não implementam suporte para plug-ins, há esforços para direcionar esses usuários por meio da compilação para JavaScript. É possível compilar o código-fonte ou o bytecode JVM para JavaScript.

Compilar o bytecode JVM, que é universal em linguagens JVM, permite construir sobre o compilador existente da linguagem para bytecode. Os principais bytecode JVM para compiladores JavaScript são TeaVM, o compilador contido no Dragome Web SDK, Bck2Brwsr e j2js-compiler.

Os principais compiladores de linguagens JVM para JavaScript incluem o compilador Java para JavaScript contido no Google Web Toolkit , Clojurescript (Clojure), GrooScript (Apache Groovy), Scala.js (Scala) e outros.

Java Runtime Environment

O Java Runtime Environment (JRE) lançado pela Oracle é uma distribuição de software disponível gratuitamente contendo um JVM autônomo (HotSpot), a biblioteca padrão Java ( Java Class Library ), uma ferramenta de configuração e - até sua descontinuação no JDK 9 - um plug-in do navegador. É o ambiente Java comum mais instalado em computadores pessoais no laptop e desktop fator de forma . Os telefones celulares, incluindo feature phones e primeiros smartphones que vêm com uma JVM, provavelmente incluem uma JVM destinada a executar aplicativos voltados para a Micro Edition da plataforma Java. Enquanto isso, a maioria dos smartphones modernos, tablets e outros PCs portáteis que executam aplicativos Java têm maior probabilidade de fazê-lo por meio do suporte do sistema operacional Android , que inclui uma máquina virtual de código aberto incompatível com a especificação JVM. (Em vez disso, as ferramentas de desenvolvimento Android do Google usam programas Java como bytecode Dalvik de entrada e saída , que é o formato de entrada nativo para a máquina virtual em dispositivos Android.)

atuação

A especificação JVM oferece muita liberdade para os implementadores em relação aos detalhes de implementação. Desde o Java 1.3, o JRE da Oracle contém uma JVM chamada HotSpot. Ele foi projetado para ser uma JVM de alto desempenho.

Para acelerar a execução do código, o HotSpot depende da compilação just-in-time. Para acelerar a alocação de objetos e coleta de lixo, HotSpot usa heap geracional.

Heap geracional

O heap da máquina virtual Java é a área de memória usada pela JVM para alocação de memória dinâmica .

No HotSpot, o heap é dividido em gerações :

  • A geração jovem armazena objetos de curta duração que são criados e imediatamente coletados no lixo.
  • Os objetos que persistem por mais tempo são movidos para a geração anterior (também chamada de geração permanente ). Essa memória é subdividida em (dois) espaços Sobreviventes, onde os objetos que sobreviveram à primeira e à próxima coleta de lixo são armazenados.

A geração permanente (ou permgen ) foi usada para definições de classe e metadados associados antes de Java 8. A geração permanente não fazia parte do heap. A geração permanente foi removida do Java 8.

Originalmente, não havia geração permanente e objetos e classes eram armazenados juntos na mesma área. Mas como o descarregamento da classe ocorre muito mais raramente do que os objetos são coletados, mover as estruturas da classe para uma área específica permitiu melhorias de desempenho significativas.

Segurança

O JRE da Oracle é instalado em um grande número de computadores. Os usuários finais com uma versão desatualizada do JRE, portanto, são vulneráveis ​​a muitos ataques conhecidos. Isso levou à crença amplamente compartilhada de que Java é inerentemente inseguro. Desde o Java 1.7, o JRE para Windows da Oracle inclui a funcionalidade de atualização automática.

Antes da descontinuação do plug-in do navegador Java, qualquer página da web poderia ter executado um miniaplicativo Java, que fornecia uma superfície de ataque facilmente acessível para sites mal-intencionados. Em 2013, a Kaspersky Labs relatou que o plug-in Java era o método de escolha para criminosos de computador. Os exploits Java estão incluídos em muitos pacotes de exploit que os hackers implantam em sites hackeados. Os miniaplicativos Java foram removidos do Java 11, lançado em 25 de setembro de 2018.

Veja também

Referências

links externos