Learn · concepts

O que é um carregador de classe Java?

Um carregador de classes encontra e carrega classes Java para a JVM em tempo de execução. Veja como eles funcionam, por que a hierarquia é importante e por que os mods dependem deles.

TRtrolPublished 5 min read

Um classloader é a parte do tempo de execução Java que encontra um arquivo de classe e o carrega na JVM. Sempre que seu código acessa um tipo pela primeira vez, a JVM pergunta a um classloader: "Encontre esta classe para mim". O carregador localiza o bytecode, lê-o e a JVM define a classe na memória. Este mecanismo é invisível em programas Java normais, mas é o ponto de fixação exato que os frameworks de mod usam para corrigir o Minecraft.

O que um classloader realmente faz

Um classloader transforma um nome de classe em uma classe utilizável dentro da JVM em execução. Você solicita um tipo, o carregador encontra o arquivo .class correspondente (ou o gera), verifica e o vincula, e a classe está pronta para uso.

O trabalho acontece em três etapas:

  1. Encontrar — O carregador localiza o bytecode para a classe nomeada (de um JAR, do disco, da rede ou gerado em tempo real)
  2. Verificar e vincular — A JVM verifica se o bytecode é válido e resolve as referências a outras classes
  3. Usar — A classe está pronta e seu programa pode instanciá-la

É por isso que os programas Java podem carregar código dinamicamente em tempo de execução que nunca foi mencionado em tempo de compilação. O nome da classe é suficiente para o carregador encontrá-lo.

A hierarquia de classloaders

Os classloaders são organizados em uma cadeia pai, e a maioria deles pergunta ao pai primeiro. Uma aplicação Java padrão tem três classloaders embutidos:

CarregadorCargasPropósito
BootstrapClasses de tempo de execução Java CoreGarante que tipos essenciais como java.lang.Object sejam sempre os mesmos
PlataformaMódulos da biblioteca padrão fora do coreMódulos e extensões Java
AplicaçãoClasses próprias do seu programaO classpath que seu programa especifica

Quando o carregador de aplicação é solicitado por uma classe, ele normalmente delega para cima primeiro. Ele pergunta ao carregador de plataforma, que pergunta ao carregador bootstrap, que tenta carregá-la. Somente se nenhum pai puder carregá-la, o filho tenta. Este é o modelo de delegação, e ele impede que a mesma classe seja carregada duas vezes com nomes diferentes.

Identidade de classe e como funciona

Uma classe é identificada tanto pelo seu nome quanto pelo carregador que a definiu. Dois carregadores diferentes podem cada um carregar uma classe chamada com.example.MyClass, e a JVM as trata como dois tipos completamente diferentes. Isso pode parecer obscuro, mas é a base de tudo que o Java pode fazer de interessante.

Bootstrap Loader: java.lang.Object (the core Object)
Application Loader: java.lang.Object (if somehow re-loaded by app loader)
→ Two different types, same name
→ This breaks your program, which is why delegation prevents it

Carregadores de classe personalizados

Programas podem definir seus próprios carregadores de classe. Um carregador personalizado pode ler bytecode de qualquer lugar: um arquivo JAR, a rede, um banco de dados ou bytecode que ele gerou ou transformou em tempo real. É assim que:

  • Sistemas de plugins carregam código dinamicamente sem saber dele em tempo de compilação.
  • Servidores de aplicativos (Tomcat, JBoss) executam múltiplos aplicativos no mesmo JVM, cada um com seu próprio classloader.
  • Mods do Minecraft corrigem classes do jogo durante o carregamento.

Por que isso importa para mods do Minecraft

Mods existem por causa do carregamento de classes personalizado. Um mod loader para Minecraft inicia o jogo através de seu próprio classloader personalizado para que possa ver e modificar as classes do jogo durante o carregamento. Esse ponto de interrupção é onde a mágica acontece.

Quando um loader fica entre o disco e o JVM, ele pode transformar uma classe logo antes do JVM defini-la:

Disk: obfuscated Minecraft class
    ↓
Mod Loader (custom classloader)
    ↓ [bytecode transformation happens here]
    ↓
JVM: defines the patched class
    ↓
Memory: game runs with mods active

Um mod pode reescrever um método do jogo, injetar novo comportamento, adicionar campos, tudo sem tocar no JAR do jogo no disco. A classe que o JVM executa é a que o framework do mod modificou. É exatamente assim que Fabric, Forge e outros mod loaders corrigem o Minecraft.

Carregamento preguiçoso

As classes não são todas carregadas na inicialização. O JVM carrega cada classe na primeira vez que ela é realmente usada. Isso é chamado de carregamento preguiçoso:

Program starts → JVM doesn't load String class yet
User calls System.out.println() → String is needed → Loader loads it

Isso significa que um classloader precisa apenas encontrar e carregar as classes que seu código realmente toca. Classes não utilizadas nunca entram na memória. É por isso que sua inicialização do Minecraft pode demorar um pouco (mods adicionam muitas classes), mas não congela para sempre.

FAQ