Mecanismo de carregamento de classe de reflexão JavaSE

Um, mecanismo de carregamento de classe

  • Quando executamos o programa com comandos Java, primeiro precisamos carregar a classe na JVM por meio do ClassLoader (ClassLoader)
  • java.lang.ClassLoader: De acordo com o nome de uma classe especificada, encontre ou gere seu código de byte correspondente e, em seguida, defina uma classe Java a partir desses códigos de byte, ou seja, uma instância da classe java.lang.Class,java.lang.ClassLoader tem dois métodos principais, loadClass (String, boolean) e findClass. O primeiro implementa o mecanismo de delegação pai, o último completa a pesquisa e o carregamento da classe. A implementação padrão de findClass é um método vazio.

Em segundo lugar, o processo de carregamento da classe

1. Carregando

  • Localize no disco rígido e leia o arquivo de código de byte (arquivos .class) via I / O, os dados binários do arquivo de classe de código de byte na memória , uma área de dados na qual a área de método de tempo de execução dentroCarregar quando a classe for usada, Como chamar o método main () da classe, o novo objeto, etc.,Na fase de carregamento, um objeto java.lang.Class representando esta classe será gerado na memória (no heap) como a entrada de acesso para vários dados desta classe na área do métodoAlém disso, se a classe principal usar outras classes durante o processo de execução, essas classes serão carregadas gradualmente. As classes no pacote jar ou no pacote war não são todas carregadas de uma vez, elas são carregadas apenas quando são usadas (No código-fonte do construtor da classe Class, o construtor é privado e apenas a JVM pode criar objetos desta classe

2. Verificação

  • Verifique a exatidão dos arquivos bytecode

3. Prepare-se

  • Atribui memória a variáveis ​​estáticas da classe e atribui valores padrão, como int atribui 4 bytes e atribui o valor 0, long atribui 8 bytes e atribui o valor 0

4. Análise

  • A referência de símbolo descreve o destino referenciado por um conjunto de símbolos. Os símbolos podem ser literais em qualquer forma. A forma literal de referência de símbolo é claramente definida no formato de arquivo de classe da especificação da máquina virtual Java. Em tempo de compilação, a classe Java não saber O endereço real da classe referenciada, então referências simbólicas só podem ser usadas em seu lugar
  • Citação direta:
  1. Apontar diretamente para o alvo
  2. Deslocamento relativo
  3. Uma alça que pode localizar indiretamente o alvo
  • Substitua as referências de símbolo por referências diretas, ou seja, a JVM substitui as referências de símbolo no pool constante por referências diretas.A análise é principalmente para classes ou interfaces, campos, métodos de classe, métodos de interface, tipos de método, identificadores de método e qualificadores de ponto de chamada. A referência de símbolo é realizada. Por exemplo, um método na classe A se refere ao método b na classe B, então ele encontrará o endereço de memória do método b na classe B e substituirá a referência de símbolo por uma referência direta (endereço de memória), que é o chamado processo de Linking estático (concluído durante o carregamento da classe), o link dinâmico é feito durante a execução do programa, substituindo as referências de símbolo por referências diretas

5. Inicialização

  • Inicialize as variáveis ​​estáticas da classe com o valor especificado e execute o bloco de código estático

5.1 Processo de inicialização do carregador de classes

  • A instância do lançador JVM sun.misc.Launcher é criada durante a execução e carregamento da classe. A inicialização do sun.misc.Launcher usa um padrão de design singleton para garantir que haja apenas uma instância sun.misc.Launcher em uma máquina virtual JVM, que está dentro do método de construção do Launcher., Criou dois carregadores de classe, a saber sun.misc.Launcher.ExtClassLoader (carregador de classe estendido) e sun.misc.Launcher.AppClassLoader (carregador de classe do aplicativo), padrões da JVM para usar o método getClassLoader () do Launcher para return Uma instância do carregador de classes AppClassLoader carrega o aplicativo

5.2 A classe não é necessariamente inicializada após ser carregada na memória, acionando o método de inicialização ativo do carregador de classes

  1. Crie uma instância do objeto, ou seja, a inicialização da classe é acionada quando o novo objeto é criado (desde que a classe não seja inicializada)
  2. Chame as propriedades estáticas da classe ou atribua valores às propriedades estáticas
  3. Chame o método estático da classe
  4. Criar objetos por meio de reflexão de arquivo de classe
  5. Inicialize uma subclasse de uma classe: primeiro inicialize a classe pai ao usar a subclasse
  6. A classe que é marcada como a classe de inicialização quando a JVM é iniciada (ou seja, a classe onde o método principal está localizado)
  • As classes só podem ser inicializadas uma vez no mesmo carregador de classes, e aquelas que foram inicializadas não precisam ser inicializadas
  • Pode ser determinado durante a compilação (constante de compilação), a classe não será inicializada
  • Não pode ser determinado em tempo de compilação (constante de execução), inicialize a classe
  • Antes que a classe seja inicializada, as primeiras 4 etapas (carregamento, verificação, preparação, análise) precisam ser realizadas
  • Se esta classe tem uma classe pai e a classe pai não foi inicializada, inicialize a classe pai primeiro
  • Se houver instruções de inicialização na classe, as instruções de inicialização são executadas por sua vez

5.3 Etapas de inicialização da classe

  • Blocos de código estáticos e propriedades estáticas são carregados no mesmo nível, ou seja, são executados na ordem em que o código é escrito

5.3.1 Nenhum pai

  1. Propriedades estáticas da classe
  2. Bloco de código estático
  3. Propriedades não estáticas da classe
  4. Bloco de código não estático
  5. Método de construção

5.3.2 Tem um pai

  1. Propriedades estáticas da classe pai
  2. O bloco de código estático da classe pai
  3. Propriedades estáticas de subclasses
  4. Bloco de código estático da subclasse
  5. Propriedades não estáticas da classe pai
  6. Bloco de código não estático da classe pai
  7. Método de construção da classe pai
  8. Propriedades não estáticas de subclasses
  9. Bloco de código não estático de subclasse
  10. Método de construção de subclasse

Terceiro, o carregador de classes

  • O processo de carregamento de classes é implementado principalmente por meio de carregadores de classes. Existem vários carregadores de classes em Java. Cada carregador de classes recebeu um diretório correspondente quando é criado. Ou seja, é determinado onde cada carregador de classes vai para carregar a classe. Além disso , o relacionamento entre os carregadores de classe não é um relacionamento de herança, mas um relacionamento de delegação
  • Da perspectiva da máquina virtual JVM, há dois carregadores de classes diferentes, o carregador de classes de inicialização e todos os outros carregadores de classes. O carregador de classes de inicialização é implementado em C ++ e faz parte da própria JVM; todos os outros carregadores de classes são implementados na linguagem Java, independentemente da máquina virtual, e todos herdam de java.lang.ClassLoader
  • Da perspectiva dos desenvolvedores, os carregadores de classes são divididos nas seguintes categorias

1. Carregador de classe de inicialização

  • Bootstrap ClassLoader é o carregador de classes de inicialização, que é responsável por carregar as bibliotecas de classes principais (jre \ lib \ rt.jar) no diretório lib do JRE que oferece suporte à operação JVM, como rt.jar, charsets.jar, etc.

2. Carregador de classes de extensão

  • Extension ClassLoader é o carregador de classes de extensão, que é responsável por carregar o pacote jar (jre \ lib \ ext * .jar) no diretório de extensão ext sob o diretório lib do JRE que suporta a operação JVM

3. Carregador de classe de aplicativo

  • Application ClassLoader é o carregador de classes do aplicativo, responsável por carregar o pacote de classes no caminho ClassPath especificado, ou seja, carregar a classe escrita por você

4. Carregador personalizado

  • User ClassLoader é um carregador personalizado, responsável por carregar pacotes de classes em caminhos definidos pelo usuário
  • A maneira de implementar um carregador de classe personalizado: herde a classe java.lang.ClassLoader e substitua o método findClass ()

4. Mecanismo de delegação parental

  • Quando uma determinada classe é carregada, ela primeiro confia no carregador pai para encontrar a classe de destino e, em seguida, confia no carregador pai superior para carregar se não puder encontrá-la. Se todos os carregadores pai não puderem encontrar a classe de destino por conta própria carregando o caminho da classe, eles estarão em sua própria classe Encontre e carregue a classe de destino no caminho de carregamento

1. Razões para criar um mecanismo de delegação dos pais

  1. Mecanismo de segurança sandbox: Por exemplo, a classe java.lang.String.class escrita por você não será carregada, para que você possa evitar que a biblioteca principal da API seja adulterada à vontade;
  2. Evite o carregamento repetido de classes: Quando o pai já carregou a classe, a subclasse ClassLoader não precisa ser carregada novamente para garantir a exclusividade da classe carregada

2. Analise o mecanismo de delegação pai no método loadClass de ClassLoader

  1. Primeiro verifique se a classe com o nome especificado foi carregada, se foi carregada, não há necessidade de carregá-la, basta retornar
  2. Se não foi carregado, determine o carregador pai, se o carregador pai atual não estiver vazio, então confie no carregador pai para carregar a classe, se o carregador pai atual estiver vazio, então confie no carregador de classe de inicialização para carregar o classe
  3. Se nem o carregador pai nem o carregador de classes de inicialização encontrarem a classe especificada, chame URLClassLoader, o método findClass do carregador de classes atual para localizar e carregar a classe no caminho de classe do carregador
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    
    
        synchronized (getClassLoadingLock(name)) {
    
    
            // First, check if the class has already been loaded
            // 检查指定名称的类是否已经加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
    
    
                long t0 = System.nanoTime();
                try {
    
    
                    if (parent != null) {
    
     // 如果当前加载器父加载器不为空则委托父加载器加载该类
                        c = parent.loadClass(name, false);
                    } else {
    
      // 如果当前加载器父加载器为空则委托引导类加载器加载该类
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
    
    
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
    
    
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    // 都会调用URLClassLoader即当前类加载器的findClass方法在加载器的类路径里查找并加载该类
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
    
     // 不会执行
                resolveClass(c);
            }
            return c;
        }
    }

Acho que você gosta

Origin blog.csdn.net/LvJzzZ/article/details/108520132
Recomendado
Clasificación