criada nos anos 90, ela é a base da plataforma Java e é 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.
gerado em tempo de runtime,buscando por possíveis melhorias para sua execução em código nativo. Nesse processo estão envolvidos dois tipos de compiladores: Client Compiler (C1) e o Server Compiler (C2).
mais rápida, o compilador busca por hot methods (mais utilizados) para fazer otimizações simples. Short lived applications também se benficiam da rapidez do C1, levando em conta que nao houve tempo de "aprender" como otimizar aquele código inicialmente.
objetivo analisar e monitorar por mais tempo o comportamento do código dentro da nossa aplicação, buscando por pontos críticos e para fazer a otimização posteriormente. As otimizações são mais agressivas já que a análise é mais rigorosa também. Métodos compilados pelo C2 são mais rápidos que os compilados pelo C1.
se tornou default, tem como estratégia começar somente com o interpretador de forma rápida e depois começar a fazer pequenas otimizações usando o C1 para saber quais são os hot methods.
da app e dos hot methods ganha maior relevância e o C2 é chamado para fazer as otimizações mais avançadas. o Tiered Compilation é dividido em 5 níveis:
modo de interpretação Nível 1 - usa o C1 de modo simples sem nenhuma análise de comportamento (profiling) Nível 2 - usa o C1 com um profiling limitado e moderado
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.
Permanent Generation Young Generation Survival Space Imagem baseada de :https://www.oracle.com/webfolder/technetwork/tutorials/ obe/java/gc01/index.html e do Livro Jornada Java , cap Garbage Collection, imagem 44.1
-> onde ficam os objetos que já "sobreviveram" às primeiras limpezas e possuem referências válidas. Old Generation -> objetos com idade mais avançada. Permanent generation -> armazena os metadados de classes e métodos (a partir do JDK 8 essa sessão foi removida e as informações que antes ficavam nela ficam alocadas diretamente em 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