criada nos anos 90 e é a base da plataforma Java. Também responsável por tratar todos os SOs e plataformas para a linguagem, ela não conhece a linguagem Java, somente o seu bytecode.
a linha é executada. A vantagem dessa abordagem é a portabilidade, que permite executar o código em diversos tipos de arquiteturas sem precisar fazer nenhum tipo de pré-compilação para a arquitetura. Por ex: Ruby e Python
e interpretada. Primeiramente o código é compilado para um formato portátil e intermediário (chamado de bytecode), para somente depois ser interpretado. Dentre as vantagens dessa abordagem podemos citar: type checking (verificação da tipagem), otimização da compilação do código, o bytecode só necessita ser compilado uma vez para chegar o mais próximo do código de máquina e mantém a portabilidade.
sua classe pai. Todos os arquivos .class relacionados a esta classe Modificadores, Métodos e Variáveis. 1. 2. 3. Temos 3 carregadores de classes integrados disponíveis em Java:
classes raiz. É a superclasse do Extension Class Loader e carrega os pacotes Java padrão (lang, util, net, io ..). Esses pacotes padrão estão presentes no arquivo rt.jar e em outras bibliotecas centrais no diretório $ JAVA_HOME / jre / lib.
do Bootstrap Class Loader e a superclasse do Application Class Loader. Carrega as extensões das bibliotecas Java padrão presentes no diretório $ JAVA_HOME / jre / lib / ext
loader e a subclasse do Extension Class Loader. Carrega os arquivos presentes no caminho de classe. O caminho de classe é definido como o diretório atual do aplicativo.
puder localizar uma classe, delega o trabalho a um class loader filho. Se o filho não for capaz de carregar a classe, ele lança noClassDefFoundError ou ClassNotFoundException.
corretamente formatado e se foi gerado por compilador válido ou não. Se essa verificação falhar, nós recebemos uma runtime exception java.lang.VerifyError. Essa verificação é feita pelo componente ByteCodeVerifier. Quando essa atividade é concluída a classe está pronta para compilação.
estáticas são atribuídas com seus valores definidos no código e no bloco estático. Esta etapa é executada de cima para baixo em uma classe de pai para filho na hierarquia de classes.
nova thread é criada na JVM, uma pilha de tempo de execução separada também é criada neste momento. (armazena informações específicas da thread criada, que será destruída assim que a thread for finalizada.
de variáveis chamado local variables. Todas local variables e seus valores são armazenados aqui. No tempo de compilação o tamanho desse array é determinado.
é compartilhado por todas as threads -> os dados armazenados na heap podem ser acessados por threads multiplas -> ponteiro para aquele objeto, que é a referência da variável e que está armazenado na pilha
ser definida como uma estrutura de dados gerenciada pela JVM -> todas as variáveis locais são criadas na pilha e são automaticamente retiradas da pilha quando você chega ao fechamento do bloco que criou aquela variável -> o dado na stack é restrito para a thread , não pode ser acessada por outras threads da aplicação.
partes do código são executadas com maior frequência. (Principalmente quais métodos são chamados com maior frequência). A execução desse código pode ser acelerada se o método já for compilado para código nativo de máquina. A parte do código que já estiver em código de máquina vai rodar mais rápido que o bytecode interpretado.
código executável que é compreendido pelo SO, na compilação o bytecode é convertido para o código de máquina nativo (isso em um thread separada), enquanto isso a JVM continua usando a versão interpretada.
Java, terminamos com nosso código- fonte compilado na representação de um bytecode JVM. Esse bytecode é mais simples e compacto que nosso código-fonte, porém os processadores convencionais em nossos computadores não podem executá-lo.
programa Java, a JVM interpreta o bytecode, como os interpretadores geralmente são muito mais lentos do que o código nativo executado em um processador real, a JVM pode executar outro compilador, que agora compilará nosso bytecode no código de máquina, que pode ser executado pelo processador.
é bem mais sofisticado que o compilador javac e executa otimizações complexas para gerar código de máquina de alta qualidade. A implementação do JDK pela Oracle foi baseada no projeto OpenJDK de código aberto. Isso inclui a máquina virtual HotSpot, disponível desde a versão 1.3 do Java. E contém dois compiladores JIT convencionais: o compilador cliente, também chamado C1, e o compilador servidor, chamado opto ou C2.
rodar mais rápido e produzir códigos menos otimizados. Enquanto C2, leva um pouco mais de tempo para rodar, porém produz um código melhor otimizado. O compilador C1 é mais adequado para aplicativos de desktop, pois não queremos longas pausas para a compilação JIT. O compilador de C2 é indicado para aplicativos de servidor de execução longa que podem gastar mais tempo na compilação.
por javac, inicia sua execução em modo interpretado. A JVM rastreia cada método chamado com frequência e os compila. Para fazer isso, ele utiliza C1 para a compilação. Porém, o HotSpot ainda observa as chamadas futuras desses métodos. Se o número de chamadas aumentar, a JVM recompilará esses métodos mais uma vez, porém desta vez usando C2.
organizar e separar os tipos de código compilados em cache, é uma área onde JVM armazena seu bytecode compilado em código nativo. Chamamos cada bloco de código nativo de nmethod , ele pode ser um método Java completo ou parte dele.
o JIT será desligado (a app não vai parar). Caso isso aconteça iremos receber o erro: "CodeCache is full… The compiler has been disabled" e teremos uma grande queda de desempenho. Para melhorar isso temporariamente, podemos mudar seu tamanho tendo as seguintes opções:
aramzenda o código interno relacionado à JVM, como o interpretador de bytecode. Por default, este segmento tem cerca de 5 MB. Além disso, é possível configurar o tamanho do segmento por meio do argumento -XX: NonNMethodCodeHeapSize
armazenda o código ligeiramente otimizado com tempos de vida potencialmente curtos. Tem como tamanho padrão 122 MB , podemos alterá-lo por meio do argumento -XX: ProfiledCodeHeapSize
: armazena o código totalmente otimizado com tempos de vida potencialmente longos. Tem cerca de 122 MB por padrão. Este valor é, obviamente, configurável por meio do argumento -XX: NonProfiledCodeHeapSize
responsável gerenciar de modo automático a alocação de memória da aplicação coordenando junto ao SO a quantidade de memória utilizada, a eliminação de objetos que já não estão mais sendo utilizados e assim determinar quando será necessário realizar uma limpeza para disponibilizar mais recursos.
responsável por gerenciar, de modo automático, a alocação de memória da aplicação coordenando junto ao SO a quantidade de memória utilizada e a eliminação de objetos que já não estão mais sendo utilizados.
simples, conhecidas como Mark and Sweep: Marcação – é aqui que o coletor de lixo identifica quais pedaços de memória estão em uso e quais não estão. Varredura – esta etapa remove objetos identificados durante a fase de “marcação”.
de objeto, essa atividade requer mais poder de CPU do que o aplicativo original. Isso pode afetar o desempenho de solicitações que exigem grande memória;
aplicações pequenas que executam em ambiente single-thread,é a implementação mais simples do GC. O argumento da JVM para usar o Serial Garbage Collector é -XX:+UseSerialGC
default de GC da JVM. Usa múltiplas threads para garbage collection, mas permanece pausado quando a aplicação está em execução. O argumento da JVM para usar o Parallel Garbage Collector é - XX:+UseParallelGC.
aplicações multi-thread que tem grande espaço de heap disponível.A heap é dividida em espaços de tamanho iguais, G1 identifica as regiões com maior “lixo” e faz a coleta nessa região O argumento da JVM para usar o G1 Garbage Collector é -XX:+UseG1GC
424: fornece uma API mais fácil de usar e mais geral para trabalhar com código e dados fora da JVM. Veio do projeto Panamá, permite acesso à memória nativa (ou seja, memória fora do heap Java) e acesso ao código nativo (por exemplo, bibliotecas C) diretamente do Java
um grande número de threads concorrentes de uma maneira que nos permite escrever código de fácil leitura e manutenção. As threads virtuais parecem threads normais de uma perspectiva de código Java, mas não são mapeadas 1:1 para as threads do sistema operacional. Existe um conjunto dos chamados threads de transporte nos quais uma thread virtual é mapeado temporariamente.
bloqueio, a thread virtual é removida da thread transportadora e a thread transportadora pode executar outra thread virtual (uma nova ou bloqueada anteriormente):