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.
em binário 🚀. Ou seja, transformam o código em linguagem de máquina, seguindo a arquitetura do sistema. Tem como vantagem a velocidade de execução pela compilação ser focada na arquitetura. Ex: C e C++
em binário conforme. 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. Ex: Ruby e Python
de linguagem. .compilada e interpretada.. Primeiramente o código é compilado para um formato portátil e intermediário (chamado de bytecode), para somente depois ser interpretado.
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 manter a portabilidade.
é convertido em bytecode (arquivo .class). Quando você tenta usar esta classe em seu aplicativo, o class loader o carrega na memória principal. Jvm armazena as seguintes informações no method area: ➔ O nome da classe carregada e sua classe pai. ➔ Todos os arquivos .class relacionados a esta classe ➔ Modificadores, Métodos e Variáveis. Class Loader
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. 1. Bootstrap class loader: Class Loader - Loading
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. 2. Extension Class Loader: Class Loader - Loading
do Extension Class Loader. Carrega os arquivos presentes no caminho de classe. O caminho de classe é definido como o diretório atual do aplicativo. 3. Application Class Loader: Class Loader - Loading
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 noC lassDefFoundError ou ClassNotFoundException. Class Loader - Loading
memória, ela vai para o processo de linking. O processo de linking envolve combinar os diferentes elementos e dependências do programa. Class Loader - Linking ➔ Linking inclui as seguintes etapas: - Verificação - Preparação - Resolução
está 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. JVM aloca memória para variáveis de classe e inicializa a memória para valores padrão. O processo de substituição de referências simbólicas, o processo é feito pesquisando no method area para alocar a entidade de referência. Class Loader - Linking
as variáveis 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.
como nome, métodos e etc. ➔ Se a memória disponível nesta área não for suficiente para a inicialização do aplicativo, a JVM lança um OutOfMemoryError. ➔ Existe apenas uma method area por JVM e é um recurso compartilhado. JVM Memory - Method Area
como nome, métodos e etc. ➔ Se a memória disponível nesta área não for suficiente para a inicialização do aplicativo, a JVM lança um OutOfMemoryError. ➔ Existe apenas uma method area por JVM e é um recurso compartilhado. JVM Memory - Method Area
como nome, métodos e etc. ➔ Se a memória disponível nesta área não for suficiente para a inicialização do aplicativo, a JVM lança um OutOfMemoryError. ➔ Existe apenas uma method area por JVM e é um recurso compartilhado. JVM Memory - Method Area
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). ➔ Variáveis locais, chamadas de método e resultados parciais são armazenados aqui. ➔ Um stackOverFlowError ocorre quando um processo que está sendo executado em uma thread requer um tamanho de pilha muito grande que não está disponível. ➔ Para cada chamada de método, uma entrada é feita na pilha de memória (stack frame) quando essa chamada de método é concluída, a stack frame é destruída. JVM Memory - JVM language Stacks (stack area)
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). ➔ Variáveis locais, chamadas de método e resultados parciais são armazenados aqui. ➔ Um stackOverFlowError ocorre quando um processo que está sendo executado em uma thread requer um tamanho de pilha muito grande que não está disponível. ➔ Para cada chamada de método, uma entrada é feita na pilha de memória (stack frame) quando essa chamada de método é concluída, a stack frame é destruída. JVM Memory - JVM language Stacks (stack area)
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). ➔ Variáveis locais, chamadas de método e resultados parciais são armazenados aqui. ➔ Um stackOverFlowError ocorre quando um processo que está sendo executado em uma thread requer um tamanho de pilha muito grande que não está disponível. ➔ Para cada chamada de método, uma entrada é feita na pilha de memória (stack frame) quando essa chamada de método é concluída, a stack frame é destruída. JVM Memory - JVM language Stacks (stack area)
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). ➔ Variáveis locais, chamadas de método e resultados parciais são armazenados aqui. ➔ Um stackOverFlowError ocorre quando um processo que está sendo executado em uma thread requer um tamanho de pilha muito grande que não está disponível. ➔ Para cada chamada de método, uma entrada é feita na pilha de memória (stack frame) quando essa chamada de método é concluída, a stack frame é destruída. JVM Memory - JVM language Stacks (stack area)
compartilhado por todas as threads; ➔ Os dados armazenados na heap podem ser acessados por threads múltiplas; ➔ Ponteiro para aquele objeto, que é a referência da variável e que está armazenado na pilha. Heap ➔ Cada thread tem sua própria stack; ➔ Pode 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. Stack
como um workspace em tempo de execução para performar operações intermediárias. JVM Memory - Operand stack Armazena todos os símbolos correspondentes ao method area e armazena as informações do bloco catch em caso de exceptions. JVM Memory - Frame Data
linha a linha. Devido a esse processo linha a linha, o interpreter é uma das etapas mais demoradas. Execution Engine - Interpreter ➔ A execution engine executa os bytecodes (arquivo .class); ➔ Lê o bytecode linha a linha; ➔ Então usa os dados e informações presentes na área de memória para executar instruções. Execution Engine
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. Execution Engine - JIT Compiler
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.
JIT ➔ Quando compilamos nosso programa 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.
JIT ➔ Para poder executar um 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.
JIT ➔ Esse chamado compilador just-in-time é 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.
JIT ➔ C1 é projetado para 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.
JIT ➔ Um programa Java, compilado 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. ➔ C2 foi extremamente otimizado e produz código capaz de competir com C++ ou ser ainda mais rápido. O próprio compilador do servidor é escrito em um dialeto específico de C++.
pela Oracle. Podemos olhar para o Graal como vários projetos conectados: um novo compilador JIT que se baseia no HotSpot e uma nova máquina virtual poliglota. ➔ Ele fornece um ecossistema abrangente que suporta um grande conjunto de linguagens (Java e outras linguagens baseadas em JVM; JavaScript, Ruby, Python, R, C/C++) Graal
um compilador em Java. Dentre elas, a segurança, que significa que não há travamentos, mas exceções, e nenhum vazamento de memória real. ➔ E o compilador pode ser independente do HotSpot e seria capaz de produzir uma versão compilada por JIT mais rápida de si mesmo. Graal
Interface – JVMCI para se comunicar com a VM. Para habilitar o uso do novo compilador JIT, precisamos definir as seguintes opções ao executar o Java a partir da linha de comando: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler Graal
encher 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: - InitialCodeCacheSize – tamanho inicial do code cache, 160K default; - ReservedCodeCacheSize – valor padrão é 48MB; - CodeCacheExpansionSize – quantidade que pode ser adicionada ao code cache, 32KB or 64KB. Segmented Code Cache
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 1 É dividido em 3 partes: Profiled-code segment: Armazena 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 2 Non-profiled segment: 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 3
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. Basicamente, o GC funciona em duas etapas 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 alocação/desalocação de memória, porque o espaço de memória não utilizado é tratado automaticamente pelo GC; • Gerenciamento automático de vazamento de memória Desvantagens: • Como a JVM precisa acompanhar a criação/exclusão de referência 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;
de GC mais simples, porque funciona basicamente com uma única thread. Como resultado, essa implementação de GC congela todos os encadeamentos do aplicativo quando é executado. O Serial GC é o GC preferido para a maioria dos aplicativos que não têm requisitos de tempo de pausa pequenos e são executados em máquinas de estilo cliente. Para habilitar o Serial Garbage Collector, podemos usar o seguinte argumento: • Java -XX:+UseSerialGC -jar Application.java
da JVM. Ao contrário do Serial Garbage Collector, ele usa várias threads para gerenciar o espaço de heap, mas também congela outros threads de aplicativos enquanto executa o GC . Se usarmos este GC, podemos especificar o máximo de threads de GC e tempo de pausa e área de cobertura (tamanho do heap). Os números de encadeamentos do coletor de lixo podem ser controlados com a opção de linha de comando -XX:ParallelGCThreads=<N>.
pausa (intervalo, em milissegundos, entre dois GCs) é especificado com a linha de comando -XX:MaxGCPauseMillis=<N> O tempo gasto na coleta de lixo versus o tempo gasto fora da coleta de lixo é chamado de destino de rendimento máximo e pode ser especificado pela linha de comando -XX:GCTimeRatio=<N>
máximo (a quantidade de memória heap que um programa necessita durante a execução) é especificado usando a opção -Xmx<N>. Para habilitar o Parallel Garbage Collector, podemos usar o seguinte argumento: • java -XX:+UseParallelGC -jar Application.java
Collector é projetado para aplicativos executados em máquinas com vários processadores com grande espaço de memória. Está disponível no JDK7 Update 4 e em versões posteriores. Ao contrário de outros coletores, o coletor G1 divide o heap em um conjunto de regiões de heap de tamanho igual, cada uma delas um intervalo contíguo de memória virtual. Para habilitar o G1 Garbage Collector, podemos usar o seguinte comando: • java -XX:+UseG1GC -jar Application.java
é um coletor de lixo escalável de baixa latência que estreou no Java 11 como uma opção experimental para Linux. O JDK 14 introduziu o ZGC nos sistemas operacionais Windows e macOS. O ZGC obteve o status de produção do Java 15 em diante. O ZGC executa todo o trabalho simultaneamente, sem interromper a execução de threads de aplicativos por mais de 10 ms, o que o torna adequado para aplicativos que exigem baixa latência. A coloração de referência é o conceito central do ZGC. Isso significa que o ZGC usa alguns bits (bits de metadados) de referência para marcar o estado do objeto.
Garbage Collector, podemos usar o seguinte argumento nas versões do JDK inferiores a 15: • java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC Application.java A partir da versão 15: • java -XX:+UseZGC Application.java