Máquina virtual Java
Máquina virtual Java (em inglês: Java Virtual Machine, JVM) é um programa que carrega e executa os aplicativos Java, convertendo os bytecodes em código executável de máquina. A JVM é responsável pelo gerenciamento dos aplicativos, à medida que são executados. Graças à máquina virtual Java, os programas escritos em Java podem funcionar em qualquer plataforma de hardware e software que possua uma versão da JVM, tornando assim essas aplicações independentes da plataforma onde funcionam.
Máquina virtual Java (em inglês: Java Virtual Machine, JVM) é um programa que carrega e executa os aplicativos Java, convertendo os bytecodes em código executável de máquina. A JVM é responsável pelo gerenciamento dos aplicativos, à medida que são executados.
Graças à máquina virtual Java, os programas escritos em Java podem funcionar em qualquer plataforma de hardware e software que possua uma versão da JVM, tornando assim essas aplicações independentes da plataforma onde funcionam.
Especificação da JVM
[editar | editar código]A máquina virtual Java é um computador abstrato (virtual) definido por uma especificação. Ela é parte do Ambiente de Execução Java (JRE). O algoritmo de coleta de lixo (garbage collection) utilizado e qualquer otimização interna das instruções da máquina virtual Java (sua tradução para linguagem de máquina) não são especificados. O principal motivo 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.[1]
Começando com o Java Platform, Standard Edition (J2SE) 5.0, as mudanças na especificação da JVM têm sido desenvolvidas sob o Java Community Process como JSR 924.[2] A partir de 2006, as mudanças na especificação para suportar alterações propostas ao formato de arquivo class (JSR 202)[3] estão sendo realizadas como uma versão de manutenção da JSR 924. A especificação para a JVM foi publicada como o livro azul (blue book),[4] cujo prefácio afirma:
Nós pretendemos que esta especificação documente suficientemente a Máquina Virtual Java para tornar possíveis implementações compatíveis do tipo clean-room. A Oracle fornece testes que verificam a operação adequada das implementações da Máquina Virtual Java.
A máquina virtual Java mais comumente utilizada é a HotSpot da Oracle.
A Oracle detém a marca registrada Java e pode permitir seu uso para certificar suítes de implementação como totalmente compatíveis com a especificação da Oracle.
Coletores de lixo
[editar | editar código]Os coletores de lixo disponíveis na máquina virtual (JVM) do OpenJDK do Java incluem:
- Serial
- Parallel
- CMS (Concurrent Mark Sweep)
- G1 (Garbage-First)
- ZGC (Z Garbage Collector)
- Epsilon
- Shenandoah
- GenZGC (Generational ZGC)
- GenShen (Generational Shenandoah)
- IBM Metronome (apenas no OpenJDK da IBM)
- SAP (apenas no OpenJDK da SAP)
- Azul C4 (Continuously Concurrent Compacting Collector)[5] (apenas no OpenJDK da Azul Systems)
| Versão | GC Padrão | GCs Disponíveis |
|---|---|---|
| 6u14 | Serial / Parallel (MP) |
Serial, Parallel, CMS, G1 (E) |
| 7u4–8 | Serial, Parallel, CMS, G1 | |
| 9–10 | G1 | |
| 11 | Serial, Parallel, CMS, G1, Epsilon (E), ZGC (E) | |
| 12–13 | Serial, Parallel, CMS, G1, Epsilon (E), ZGC (E), Shenandoah (E) | |
| 14 | Serial, Parallel, G1, Epsilon (E), ZGC (E), Shenandoah (E) | |
| 15–20 | Serial, Parallel, G1, Epsilon (E), ZGC, Shenandoah | |
| 21–22 | Serial, Parallel, G1, Epsilon (E), ZGC, Shenandoah, GenZGC (E) | |
| 23 | Serial, Parallel, G1, Epsilon (E), ZGC, Shenandoah, GenZGC (ZGC padrão) | |
| 24 | Serial, Parallel, G1, Epsilon (E), Shenandoah, GenZGC, GenShen (E) | |
| 25 | Serial, Parallel, G1, Epsilon (E), Shenandoah, GenZGC, GenShen | |
| (E) = experimental | ||
A filosofia de design do Java gira em torno da suposição de um coletor de lixo. Ao contrário de linguagens como C++ e Rust, o gerenciamento determinístico de memória através de uma palavra-chave delete (como no C++) não é possível. Mesmo introduzir tal recurso não seria viável, devido à falta de propriedade (ownership), exceto pelo uso de sun.misc.Unsafe ou através da java.lang.foreign para alocar/desalocar memória fora do heap Java.[6][7] Como o Java utiliza principalmente a alocação baseada em heap, os objetos são armazenados como referências, e a exclusão manual resultaria em ponteiros pendentes.
Foo a = new Foo();
Foo b = a;
// Em Java, não existe 'delete a'
// Se existisse, 'b' se tornaria um ponteiro pendente
System.out.println(b);
Arquitetura da máquina virtual
[editar | editar código]A JVM opera em tipos específicos de dados, conforme especificado nas especificações da Máquina Virtual Java. Os tipos de dados podem ser divididos[8] em tipos primitivos (valores inteiros e de ponto flutuante) e tipos de referência. Os tipos long e double, que possuem 64 bits, são suportados nativamente, mas consomem duas unidades de armazenamento nas variáveis locais de um quadro (frame) ou na pilha de operandos, uma vez que cada unidade possui 32 bits. Os tipos boolean, byte, short e char são todos estendidos por sinal (exceto o char, que é estendido por zero) e operados como inteiros de 32 bits, da mesma forma que os tipos int. Os tipos menores possuem apenas algumas instruções específicas para carregamento, armazenamento e conversão de tipo. O boolean é operado como valores byte de 8 bits, com o 0 representando false e o 1 representando true. (Embora o boolean tenha sido tratado como um tipo desde que The Java Virtual Machine Specification, Second Edition esclareceu essa questão, no código compilado e executado há pouca diferença entre um boolean e um byte, exceto pela codificação de nomes (name mangling) nas assinaturas de métodos e no tipo de arranjos (arrays) booleanos. Os booleans em assinaturas de métodos são codificados como Z, enquanto os bytes são codificados como B. Arranjos booleanos carregam o tipo boolean[], mas usam 8 bits por elemento, e a JVM não possui capacidade embutida para empacotar booleanos em um arranjo de bits, portanto, exceto pelo tipo, eles funcionam e se comportam da mesma forma que arranjos de byte. Em todos os outros usos, o tipo boolean é efetivamente desconhecido para a JVM, já que todas as instruções para operar em booleanos também são usadas para operar em bytes.)
A JVM possui um heap com coleta de lixo para armazenar objetos e arranjos. O código, as constantes e outros dados da classe são armazenados na "área de métodos". A área de métodos é logicamente parte do heap, mas as implementações podem tratar a área de métodos separadamente do heap e, por exemplo, não realizar a coleta de lixo nela. Cada thread da JVM também tem sua própria pilha de chamadas (chamada de "pilha da Máquina Virtual Java" por clareza), que armazena quadros (frames). 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 um arranjo de "variáveis locais". A pilha de operandos é usada para operandos executarem cálculos e para receber o valor de retorno de um método chamado, enquanto as variáveis locais servem ao mesmo propósito que os registradores e também são usadas para passar argumentos de métodos. Dessa forma, a JVM é ao mesmo tempo uma máquina de pilha e uma máquina de registradores. Na prática, a HotSpot elimina todas as pilhas, exceto a pilha de chamadas/thread nativa, mesmo ao rodar no modo Interpretado, já que o seu Interpretador de Templates atua tecnicamente como um compilador.
A JVM usa referências e índices de pilha/arranjos para endereçar dados; ela não utiliza o endereçamento por bytes como a maioria das máquinas físicas faz, portanto não se enquadra perfeitamente na categorização usual de máquinas de 32 bits ou 64 bits. Em um sentido, ela poderia ser classificada como uma máquina de 32 bits, já que esse é o tamanho do maior valor que ela armazena nativamente: um número inteiro de 32 bits, um valor de ponto flutuante ou uma referência de 32 bits. Como uma referência tem 32 bits, cada programa é limitado a no máximo 232 referências únicas e, portanto, no máximo 232 objetos. No entanto, cada objeto pode ter mais de um byte e ser potencialmente muito grande; o maior objeto possível é um arranjo de long com tamanho de 231 - 1, o que consumiria 16 GiB de memória, e poderia haver potencialmente 232 destes se houvesse memória suficiente disponível. Isso resulta em limites superiores mais comparáveis a uma máquina típica de 64 bits endereçável por byte. Uma implementação da JVM pode ser projetada para rodar em um processador que usa nativamente qualquer largura de bits, desde que implemente corretamente a matemática inteira (8, 16, 32 e 64 bits) e de ponto flutuante (32 e 64 bits) exigida pela JVM. Dependendo do método usado para implementar as referências (ponteiros nativos, ponteiros comprimidos ou uma tabela de indireção), isso pode limitar o número de objetos a algo menor do que o máximo teórico. Uma implementação da JVM em uma plataforma de 64 bits tem acesso a um espaço de endereçamento muito maior do que uma em plataforma de 32 bits, o que permite um tamanho de heap muito maior e um aumento no número máximo de threads, algo necessário para certos tipos de aplicações grandes; no entanto, pode haver uma queda de desempenho ao utilizar uma implementação de 64 bits em comparação com uma de 32 bits.
Linguagens da JVM
[editar | editar código]Uma linguagem da JVM é qualquer linguagem com funções que podem ser expressas em um arquivo class válido, o qual pode ser hospedado por uma JVM. Um arquivo class contém instruções da JVM (Bytecode Java), uma tabela de símbolos e outras informações complementares. O formato de arquivo class é o formato binário independente de hardware e sistema operacional usado para representar classes e interfaces compiladas.[9]
Existem diversas linguagens da JVM, tanto linguagens estabelecidas e mais antigas portadas para a JVM, quanto linguagens muito mais recentes criadas do zero. O JRuby e o Jython talvez sejam os ports mais conhecidos de linguagens antigas, ou seja, Ruby e Python, respectivamente. Entre as novas linguagens criadas do zero para compilar em bytecode Java, Clojure, Apache Groovy, Scala e Kotlin são as mais populares. Uma característica notável das linguagens da JVM é sua interoperabilidade de linguagens; elas são compatíveis umas com as outras, para que, por exemplo, bibliotecas Scala possam ser usadas com programas Java e vice-versa.[10]
A JVM do Java 7 implementa a JSR 292: Supporting Dynamically Typed Languages[11] na Plataforma Java, um novo recurso que suporta linguagens dinamicamente tipadas na JVM. Esse recurso é desenvolvido dentro do projeto Máquina Da Vinci (Da Vinci Machine), cuja missão é estender a JVM para suportar outras linguagens além do Java.[12][13]
Carregador de classes
[editar | editar código]Uma das unidades organizacionais do código de bytes (bytecode) da JVM é a classe. Uma implementação de carregador de classes (class loader) deve ser capaz de reconhecer e carregar qualquer item que esteja em conformidade com o formato de arquivo class do Java. Qualquer implementação é livre para reconhecer outros formatos binários além de arquivos class, mas deve obrigatoriamente reconhecer os arquivos class.
O carregador de classes executa três atividades básicas nesta ordem estrita:
- Carregamento (Loading): localiza e importa os dados binários para um tipo.
- Vinculação (Linking): executa a verificação, preparação e (opcionalmente) resolução.
- Verificação: assegura a correção do tipo importado.
- Preparação: aloca memória para variáveis de classe e inicializa a memória com valores padrão.
- Resolução: transforma referências simbólicas do tipo em referências diretas.
- Inicialização (Initialization): invoca o código Java que inicializa as variáveis de classe em seus devidos valores iniciais.
Em geral, existem três tipos de carregadores de classe: carregador de classes de inicialização (bootstrap class loader), carregador de classes de extensão (extension class loader) e carregador de classes de sistema / aplicativo (System / Application class loader).
Cada implementação da máquina virtual Java deve possuir um carregador de classes de inicialização capaz de carregar classes confiáveis, bem como um carregador de classes de extensão ou de aplicativo. A especificação da máquina virtual Java não estipula como um carregador de classes deve localizar as classes.
Instruções de bytecode
[editar | editar código]A JVM tem instruções para os seguintes grupos de tarefas:
- Carregamento e armazenamento (Load / store)
- Aritmética
- Conversão de tipo
- Criação e manipulação de objetos
- Gerenciamento da pilha de operandos (push / pop)
- Transferência de controle (desvios / branching)
- Invocação e retorno de métodos
- Lançamento de exceções (Throwing exceptions)
- Concorrência baseada em monitores
O objetivo é a compatibilidade binária. Cada sistema operacional hospedeiro em particular precisa de sua própria implementação da JVM e ambiente de execução. Essas JVMs interpretam o bytecode de maneira semanticamente igual, mas a implementação real pode ser diferente. Mais complexo do que apenas emular o bytecode é implementar de forma compatível e eficiente a API principal do Java, a qual deve ser mapeada para cada sistema operacional hospedeiro.
Essas instruções operam em um conjunto comum de tipos de dados abstratos, em vez dos tipos de dados nativos de qualquer arquitetura de conjunto de instruções específica.
Verificador de bytecode
[editar | editar código]Uma filosofia fundamental do Java é que ele é inerentemente seguro do ponto de vista de que nenhum programa de usuário pode derrubar (crash) a máquina hospedeira ou interferir de maneira inadequada com outras operações nela, e que é possível proteger determinados métodos e estruturas de dados pertencentes a códigos confiáveis do acesso ou corrupção por códigos não confiáveis em execução na mesma JVM. Além disso, erros comuns de programação que frequentemente levam à corrupção de dados ou comportamentos imprevisíveis, como acessar fora do limite de um arranjo ou usar um ponteiro não inicializado, não têm permissão para ocorrer. Vários recursos do Java se combinam para fornecer essa segurança, incluindo o modelo de classes, o heap com coleta de lixo e o verificador.
A JVM verifica todo o bytecode antes de ele ser executado. Essa verificação consiste primariamente em três tipos de checagens:
- Os desvios (branches) sempre apontam para locais válidos.
- Os dados são sempre inicializados e as referências são sempre seguras quanto aos tipos (type-safe).
- O acesso a dados e métodos privados ou com visibilidade de pacote (package private) é rigidamente controlado.
As duas primeiras dessas checagens ocorrem principalmente durante a etapa de verificação, que acontece quando uma classe é carregada e se torna elegível para uso. A terceira é executada principalmente de forma dinâmica, quando 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 dada opere em um local fixo na pilha,[14] permitindo que o compilador JIT transforme acessos à pilha em acessos a registradores fixos. Por causa disso, o fato de a JVM ser uma arquitetura de pilha não implica penalidade de velocidade para emulação em arquiteturas baseadas em registradores quando se utiliza um compilador JIT. Diante da arquitetura JVM com código verificado, não faz diferença para um compilador JIT se ele recebe registradores imaginários nomeados ou posições de pilha imaginárias que devem ser alocadas nos registradores da arquitetura alvo. Na verdade, a verificação de código torna a JVM diferente de uma arquitetura clássica de pilha, cuja emulação eficiente com um compilador JIT é mais complicada e tipicamente realizada por um interpretador mais lento. Adicionalmente, o Interpretador utilizado pela JVM padrão é um tipo especial conhecido como Interpretador de Templates (Template Interpreter), que traduz o bytecode diretamente para a linguagem de máquina nativa baseada em registradores em vez de emular uma pilha como um interpretador típico faria.[15] Em muitos aspectos, o Interpretador da HotSpot pode ser considerado um compilador JIT em vez de um verdadeiro interpretador, significando que a arquitetura de pilha voltada pelo bytecode não é de fato utilizada na implementação, mas serve apenas como uma especificação para a representação intermediária que pode muito bem ser implementada em uma arquitetura baseada em registradores. Outro exemplo de uma arquitetura de pilha que é meramente uma especificação implementada numa máquina virtual baseada em registradores é a Common Language Runtime.[16]
A especificação original para o verificador de bytecode usava uma linguagem natural que era incompleta ou incorreta em alguns aspectos. Diversas tentativas têm sido feitas para especificar a JVM como um sistema formal. Fazendo isso, a segurança das implementações atuais da JVM pode ser analisada de forma mais aprofundada, e possíveis explorações de segurança (exploits) evitadas. Também será possível otimizar a JVM pulando checagens de segurança desnecessárias, caso a aplicação sendo executada seja comprovadamente segura.[17]
Execução segura de código remoto
[editar | editar código]Uma arquitetura de máquina virtual permite um controle de granularidade muito fina sobre as ações que o código dentro da máquina está autorizado a tomar. Pressupõe-se que o código é "semanticamente" correto, isto é, passou com sucesso pelo processo de verificador (formal) de bytecode, materializado por uma ferramenta, possivelmente fora da própria máquina virtual. Isso foi projetado para permitir a execução segura de códigos não confiáveis oriundos de fontes remotas, um modelo usado por applets Java e outros downloads de código seguro. Uma vez verificado seu bytecode, o código baixado é executado em um ambiente restrito (sandbox), projetado para proteger o usuário de códigos maliciosos ou com mau comportamento. Como acréscimo ao processo de verificação de bytecode, os publicadores podem adquirir um certificado com o qual os applets são assinados digitalmente como seguros, concedendo-lhes permissão para pedir ao usuário a quebra das restrições do sandbox e acessar o sistema de arquivos local, a área de transferência (clipboard), executar partes externas de software ou usar a rede.
Uma prova formal de verificadores de bytecode foi feita pela indústria do Javacard (Desenvolvimento Formal de um Verificador Incorporado para Byte Code Java Card[18]).
Interpretador de bytecode e compilador Just-in-Time
[editar | editar código]Para cada arquitetura de hardware, é necessário um interpretador de bytecode Java diferente. Quando um computador possui um interpretador de bytecode Java, ele consegue executar qualquer programa em bytecode Java, e o mesmo programa pode ser executado em qualquer computador que tenha tal interpretador.
Quando o bytecode Java é executado por um interpretador, a execução será sempre mais lenta do que a execução do mesmo programa compilado em linguagem de máquina nativa. Esse problema é mitigado pelos compiladores Just-in-Time (JIT) para a execução de bytecode Java. Um compilador JIT pode traduzir o 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 nas partes do programa executadas frequentemente. Dessa forma, um compilador JIT pode acelerar de maneira significativa o tempo total de execução.
Não há uma conexão necessária entre a linguagem de programação Java e o bytecode Java. Um programa escrito em Java pode ser compilado diretamente para a linguagem de máquina de um computador real, e programas escritos em outras linguagens além do Java podem ser compilados em bytecode Java.
O bytecode Java foi concebido para ser independente de plataforma e seguro.[19] Algumas implementações da JVM não incluem um interpretador, mas consistem apenas de um compilador JIT.[20]
Java Native Interface
[editar | editar código]A Java Native Interface (Interface Nativa do Java), ou Native Method Interface (Interface de Método Nativo), é um framework de software de programação com interface de função estrangeira (não-Java) que permite que o código Java executado em uma Máquina Virtual Java (JVM) chame e seja chamado por aplicativos e bibliotecas nativas (programas específicos a uma plataforma de hardware e sistema operacional) escritos em outras linguagens, tais como C, C++ e Assembly.
O Java 22 introduz a Foreign Function and Memory API (API de Função Estrangeira e Memória), que pode ser vista como o sucessor da Java Native Interface.
Os métodos nativos são habilitados pela JNI para lidar com situações em que uma aplicação não pode ser escrita inteiramente na linguagem de programação Java, por exemplo, quando a biblioteca de classes padrão do Java não oferece suporte para os recursos ou biblioteca de programas específicos da plataforma.
A estrutura do JNI permite que um método nativo use objetos Java da mesma forma que um código Java os usa. Um método nativo pode criar objetos Java e então inspecionar e usá-los para realizar suas tarefas. Um método nativo também pode inspecionar e usar objetos criados pelo código da aplicação Java.
A JNI também permite acesso direto ao código assembly, mesmo sem passar por uma ponte em C. Acessar aplicativos Java pelo assembly também é possível da mesma forma.
A JVM no navegador web
[editar | editar código]No início da vida útil da plataforma Java, a JVM era comercializada como uma tecnologia web para a criação de Aplicações Web Ricas (Rich Web Applications). A partir de 2018, a maioria dos navegadores web e sistemas operacionais que vêm com navegadores embutidos não inclui um plug-in do Java, tampouco permite a instalação paralela (sideloading) de qualquer plug-in que não seja o Flash. O plug-in do Java para navegadores foi descontinuado no JDK 9.[21]
O plug-in do Java para navegadores baseado na NPAPI foi projetado para permitir que a JVM executasse os chamados applets Java incorporados em páginas HTML. Para navegadores com o plug-in instalado, é permitido ao applet desenhar em uma região retangular da página atribuída a ele. Como o plug-in inclui uma JVM, os applets Java não estão restritos à linguagem de programação Java; qualquer linguagem voltada para a JVM pode rodar no plug-in. Um conjunto restrito de APIs permite aos applets acesso ao microfone do usuário ou à aceleração 3D, embora os applets não consigam modificar a página fora de sua região retangular. O Adobe Flash Player, a principal tecnologia concorrente, funciona da mesma maneira nesse aspecto.
Em junho de 2015, de acordo com a W3Techs, o uso de applets Java e do Silverlight havia caído para 0,1% cada em todos os sites, enquanto o Flash havia caído para 10,8%.[22]
JVMs e interpretadores em JavaScript
[editar | editar código]Desde maio de 2016, o JavaPoly permite aos usuários importar bibliotecas Java não modificadas e invocá-las diretamente a partir do JavaScript. O JavaPoly permite que sites usem bibliotecas Java não modificadas, mesmo que o usuário não tenha o Java instalado em seu computador.[23]
Transpilação para JavaScript
[editar | editar código]Com as contínuas melhorias na velocidade de execução do JavaScript, combinadas com o uso crescente de dispositivos móveis cujos navegadores não implementam suporte para plug-ins, existem esforços para atingir esses usuários através da transpilação (transpilation) para JavaScript. É possível transpilar o código-fonte ou o bytecode da JVM para JavaScript.
A compilação do bytecode da JVM, que é universal entre as linguagens da JVM, permite aproveitar o compilador existente da linguagem para bytecode. Os principais transpiladores de bytecode da JVM para JavaScript são o TeaVM,[24] o compilador contido no Dragome Web SDK,[25] o Bck2Brwsr,[26] e o j2js-compiler.[27]
Os principais transpiladores de linguagens da JVM para JavaScript incluem o transpilador de Java para JavaScript contido no Google Web Toolkit, o J2CL,[28] o Clojurescript (Clojure), o GrooScript (Apache Groovy), o Scala.js (Scala) e outros.[29]
Ver também
[editar | editar código]Referências
- ↑ Bill Venners, Inside the Java Virtual Machine Capítulo 5, arquivado em 25 de janeiro de 2021 no Wayback Machine.
- ↑ «The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 924». Jcp.org. Consultado em 26 de junho de 2015. Cópia arquivada em 24 de dezembro de 2020
- ↑ «The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 202». Jcp.org. Consultado em 26 de junho de 2015. Cópia arquivada em 26 de fevereiro de 2012
- ↑ The Java Virtual Machine Specification (a primeira e a segunda edições também estão disponíveis online), arquivado em 9 de julho de 2008 no Wayback Machine.
- ↑ Tene, Gil; Iyengar, Balaji; Wolf, Michael (2011). «C4: the continuously concurrent compacting collector». ISMM '11: Proceedings of the international symposium on Memory management. [S.l.: s.n.] ISBN 978-1-45030263-0. doi:10.1145/1993478. Cópia arquivada (PDF) em 9 de agosto de 2017
|arquivourl=requer|url=(ajuda) - ↑ Evans, Ben (4 de maio de 2020). «The Unsafe Class: Unsafe at Any Speed». blogs.oracle.com. Oracle Corporation
- ↑ «JEP 454: Foreign Function & Memory API». OpenJDK. 7 de abril de 2025
- ↑ «Chapter 2. The Structure of the Java Virtual Machine». Consultado em 15 de setembro de 2021. Cópia arquivada em 15 de setembro de 2021
- ↑ «The Java Virtual Machine Specification: Java SE 7 Edition» (PDF). Docs.oracle.com. Consultado em 26 de junho de 2015. Cópia arquivada (PDF) em 4 de fevereiro de 2021
- ↑ «Frequently Asked Questions - Java Interoperability». scala-lang.org. Consultado em 18 de novembro de 2015. Cópia arquivada em 9 de agosto de 2020
- ↑ «The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 292». Jcp.org. Consultado em 26 de junho de 2015. Cópia arquivada em 20 de dezembro de 2020
- ↑ «Da Vinci Machine project». Openjdk.java.net. Consultado em 26 de junho de 2015. Cópia arquivada em 11 de novembro de 2020
- ↑ «New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine». Oracle.com. Consultado em 26 de junho de 2015. Cópia arquivada em 13 de setembro de 2018
- ↑ «The Verification process». The Java Virtual Machine Specification. Sun Microsystems. 1999. Consultado em 31 de maio de 2009. Cópia arquivada em 21 de março de 2011
- ↑ «HotSpot Runtime Overview - Interpreter». OpenJDK. Consultado em 24 de maio de 2021. Cópia arquivada em 21 de maio de 2022
- ↑ «Why not make CLR register-based? · Issue #4775 · dotnet/runtime». GitHub. Consultado em 24 de maio de 2021. Cópia arquivada em 20 de abril de 2023
- ↑ Freund, Stephen N.; Mitchell, John C. (1999). «A formal framework for the Java bytecode language and verifier». Proceedings of the 14th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications - OOPSLA '99. [S.l.: s.n.] pp. 147–166. ISBN 978-1581132380. doi:10.1145/320384.320397
- ↑ Casset, Ludovic; Burdy, Lilian; Requet, Antoine (10 de abril de 2002). «Formal Development of an Embedded Verifier for Java Card Byte Code» (PDF). Inria - National Institute for Research in Digital Science and Technology na Universidade Côte d'Azur. Cópia arquivada (PDF) em 3 de outubro de 2022
- ↑ David J. Eck, Introduction to Programming Using Java, Sétima Edição, Versão 7.0, agosto de 2014 na Seção 1.3 "The Java Virtual Machine", arquivado em 11 de outubro de 2014 no Wayback Machine.
- ↑ Oracle JRockit Introduction Versão R28 em 2. "Understanding Just-In-Time Compilation and Optimization", arquivado em 6 de setembro de 2015 no Wayback Machine.
- ↑ «Oracle deprecates the Java browser plugin, prepares for its demise». Ars Technica. 28 de janeiro de 2016. Consultado em 15 de abril de 2016. Cópia arquivada em 8 de abril de 2016
- ↑ «Historical yearly trends in the usage of client-side programming languages, June 2015». W3techs.com. Consultado em 26 de junho de 2015
- ↑ Krill, Paul (13 de maio de 2016). «JavaPoly.js imports existing Java code and invokes it directly from JavaScript». InfoWorld. Consultado em 18 de julho de 2016. Cópia arquivada em 25 de julho de 2016
- ↑ «TeaVM project home page». Teavm.org. Consultado em 26 de junho de 2015. Cópia arquivada em 27 de junho de 2015
- ↑ «Dragome Web SDK». Dragome.com. Consultado em 26 de junho de 2015. Cópia arquivada em 1 de agosto de 2015
- ↑ «Bck2Brwsr - APIDesign». Wiki.apidesign.org. Consultado em 26 de junho de 2015. Cópia arquivada em 27 de junho de 2015
- ↑ Wolfgang Kuehn (decatur). j2js-compiler, arquivado em 29 de setembro de 2013 no Wayback Machine. GitHub
- ↑ «J2cl: Transpile Java to JavaScript for Closure Compiler | Hacker News». news.ycombinator.com (em inglês). Consultado em 31 de outubro de 2025. Cópia arquivada em 2 de janeiro de 2021
- ↑ «List of languages that compile to JS · jashkenas/coffeescript Wiki · GitHub». Github.com. 19 de junho de 2015. Consultado em 26 de junho de 2015. Cópia arquivada em 31 de janeiro de 2020
Ligações externas
[editar | editar código]- «Especificações da JVM» (em inglês)
- «Segunda edição da especificação da JVM» (em inglês)
- «Grupo sobre Java HotSpot Virtual Machine» (em inglês)
- «Descargar máquina virtual Java» (em inglês)
- «OpenSource JDK» (em inglês)