Uma tradução do artigo foi preparada especificamente para os alunos do curso Java Developer .
Nesta série de artigos, falarei sobre como a Java Virtual Machine funciona. Hoje, examinamos o mecanismo para carregar classes na
JVM .
A Java Virtual Machine está no coração do ecossistema de tecnologia Java. Isso possibilita que os programas Java implementem o princípio "a gravação é executada em qualquer lugar". Como outras máquinas virtuais, a JVM é um computador abstrato. A principal tarefa da JVM é carregar
arquivos de classe e executar o
bytecode contido neles.
A JVM inclui vários componentes, como
um carregador de classes ,
Garbage Collector (gerenciamento automático de memória), um intérprete, um compilador
JIT e componentes de controle de fluxo. Neste artigo, veremos o carregador de classes.
O carregador de classes carrega arquivos de classe para seu aplicativo e a API Java. Somente os arquivos de classe da API Java que são realmente necessários ao executar o programa são carregados na máquina virtual.
O código de bytes é executado pelo mecanismo de execução.

O que é carregamento de classe?
Carregamento de classe é a pesquisa e o carregamento de tipos (classes e interfaces) dinamicamente durante a execução do programa. Os dados de tipo estão em arquivos de classe binária.
Etapas de carregamento de classe
O subsistema do carregador de classes não é apenas responsável por localizar e importar dados binários da classe. Ele também executa a validação de classes importadas, aloca e inicializa a memória para variáveis de classe e ajuda na resolução de links simbólicos. Essas ações são executadas na seguinte ordem:
- Carregando - pesquisando e importando dados binários para um tipo por seu nome, criando uma classe ou interface a partir dessa representação binária.
- Vinculação (vinculação) - verificação, preparação e, opcionalmente, permissão:
- Verificação - verificação da correção do tipo importado.
- Preparação - alocação de memória para variáveis de classe estática e inicialização de memória com valores padrão.
- Resolução - Converta links de tipo simbólico em links diretos.
- Inicialização é uma chamada ao código Java que inicializa variáveis de classe com seus valores iniciais corretos.
Nota - o carregador de classes, além de carregar classes, também é responsável por encontrar recursos. Um recurso são alguns dados (por exemplo, um arquivo “.class”, dados de configuração, imagens) que são identificados usando um caminho abstrato, separado por um caractere “/”. Os recursos são geralmente empacotados com um aplicativo ou biblioteca para que possam ser usados no código do aplicativo ou da biblioteca.
Mecanismo de carregamento de classe em Java
Nota do tradutor - esta seção descreve o comportamento do java <9; no java 9+, ocorreram pequenas alterações, descritas a seguir.Java usa o modelo de delegação de carregamento de classe. A idéia básica é que cada carregador de classes tenha um carregador "pai". Quando uma
classe está sendo
carregada , o carregador “delega” a pesquisa de classe para seu pai antes de procurar a classe por conta própria.
O modelo de delegação do carregador de classes é um gráfico de carregadores que passam solicitações de carga entre si. A raiz deste gráfico é o carregador de inicialização. Carregadores de classe são criados com um pai ao qual eles podem delegar o carregamento e procure a classe nos seguintes locais:
- Cache
- O pai
- Próprio carregador de inicialização
O carregador de classes primeiro verifica se ele carregou a classe antes. Nesse caso, a mesma classe retornada na última vez é retornada (a classe armazenada no cache). Caso contrário, o pai terá a oportunidade de carregar a classe. Essas duas etapas são repetidas recursivamente em profundidade. Se o pai retornar nulo (ou lançar uma
ClassNotFoundException ), o carregador procurará a classe por conta própria.
A classe é carregada pelo carregador mais próximo da raiz, pois o direito de primeiro carregar a classe sempre é concedido ao carregador pai. Isso permite que o carregador veja apenas as classes carregadas independentemente, por seus pais ou ancestrais. Ele não pode ver as classes carregadas pelos carregadores filhos.
A API da plataforma Java SE historicamente definiu dois carregadores de classe:
Carregador de classes de bootstrap
(carregador básico e primário) - carrega classes do caminho de classe de bootstrap.
Carregador de classes do sistema (carregador pai
) - a classe pai dos novos carregadores de classes e, como regra, o carregador de classes usado para carregar e executar o aplicativo.
Carregadores de classe JDK 9+
Carregador de classes de aplicativos - normalmente usado para carregar classes de aplicativos de um caminho de classe. É também o carregador de inicialização padrão para alguns módulos JDK que contêm utilitários ou exportam a API de utilitários. (
Nota do tradutor: por exemplo, jdk.jconsole
, jdk.jshell
, etc. )
Carregador de classe de plataforma - carrega os módulos Java SE e JDK selecionados (com base na segurança / permissões). Por exemplo, java.sql.
Carregador de classe Bootstrap - Carrega os principais módulos Java SE e JDK.
Esses três carregadores de classe internos funcionam juntos da seguinte maneira:
- O carregador de classes de aplicativos primeiro procura módulos nomeados definidos para todos os carregadores internos. Se um módulo adequado for definido para um desses carregadores, esse carregador carregará a classe. Se a classe não for encontrada no módulo nomeado definido para um desses carregadores, o carregador de classes do aplicativo a delega ao pai. Se a classe não for encontrada pelo pai, o carregador de classes do aplicativo a procurará no caminho de classe. As classes encontradas no caminho de classe são carregadas como membros do módulo sem nome deste carregador.
- O carregador de classes de plataforma procura por módulos nomeados definidos para todos os carregadores internos. Se um módulo adequado for definido para um desses carregadores, esse carregador carregará a classe. Se a classe não for encontrada no módulo nomeado definido para um desses carregadores, o carregador de classes da plataforma a delegará ao pai.
- O carregador de classes de autoinicialização procura módulos nomeados definidos por si. Se a classe não for encontrada no módulo nomeado definido para o carregador de inicialização, o carregador de inicialização procurará por arquivos e diretórios adicionados ao caminho de classe usando o parâmetro -Xbootclasspath / a (permite adicionar arquivos e diretórios ao caminho de classe de inicialização). As classes encontradas no caminho de classe de autoinicialização são carregadas como membros do módulo sem nome deste carregador.
Para visualizar os carregadores de classes internos, você pode usar o seguinte código:
package ru.deft.homework; import java.sql.Date; public class BuiltInClassLoadersDemo { public static void main(String[] args) { BuiltInClassLoadersDemo demoObject = new BuiltInClassLoadersDemo(); ClassLoader applicationClassLoader = demoObject.getClass().getClassLoader(); printClassLoaderDetails(applicationClassLoader);
Ao executar esse código no Amazon Corretto 11.0.3 instalado em mim, obtemos o seguinte resultado:
ClassLoader name : app ClassLoader class : jdk.internal.loader.ClassLoaders$AppClassLoader ClassLoader name : platform ClassLoader class : jdk.internal.loader.ClassLoaders$PlatformClassLoader Bootstrap classloader
Você pode aprender mais sobre a API do ClassLoader
aqui (JDK 11) .