Prefácio
Todos devem saber que, quando um projeto Java for iniciado, a JVM encontrará o método principal e carregará o arquivo de classe e o arquivo de classe no pacote JAR referenciado de acordo com as chamadas entre os objetos (as etapas são divididas em carregamento, verificação, preparação, análise, inicialização, uso e descarregamento). A área do método abre a memória para armazenar a estrutura de dados de tempo de execução da classe (incluindo variáveis estáticas, métodos estáticos, pools constantes, estruturas de classe etc.) e, ao mesmo tempo, o objeto de classe correspondente é gerado na pilha para apontar para a estrutura de dados de tempo de execução da classe correspondente na área do método.
Para resumir na frase mais simples, o processo de carregamento de classe é que a JVM lê o arquivo ByteCode Class por meio de fluxos de IO com base no caminho do arquivo de classe necessário e injeta -o na memória através de uma série de etapas de análise e inicialização. Os carregadores de classe em Java incluem: BootstrapClassLoader (camada superior), ExtclassLoader, AppClassLoader e ClassLoader definido pelo usuário (camada inferior). Para diferentes tipos de pacotes JAR (ou arquivos de classe), a JVM terá diferentes tipos de carregadeiras de classe para carregar.
O relacionamento correspondente é o seguinte:
O BootstrapClassLoader é usado para carregar as classes necessárias para a JVM em execução:
Java_home/jre/lib/resources.jar: java_home/jre/lib/rt.jar: java_home/jre/lib/sunRasign.jar: java_home/jre/lib/jsse.jar: java_home/jre/lib/jce.jar/jcear: Java_home/jre/lib/jfr.jar: java_home/jre/lib/classes
ExtclassLoader é usado para carregar classes de extensão:
../Java/extensions: ../java_home/jre/lib/ext: ../library/java/extensions:/network/library/java/extensions: ../system/library/java/extensio
O AppClassLoader é usado para carregar as classes criadas no ClassPath e as classes referenciadas no pacote JAR em nosso projeto.
Todo o carregamento da classe é carregado através de um mecanismo chamado delegação pai.
Por exemplo, uma classe é carregada pelo carregador de nível mais baixo (carregador de classe definido pelo usuário). Este carregador ligará primeiro para o carregador de nível anterior (AppClassLoader) para carregamento, e o AppClassLoader continuará sendo entregue ao nível superior (EXTClassLoader) para carregar até o bootstrapclassloader. Se o caminho de classe carregado pelo BootstrapClassLoader não puder encontrar esta classe, ele será entregue ao carregador (extclassloader) da próxima camada para carregar. Se essa classe não puder ser encontrada, continuará sendo entregue à próxima camada (AppClassLoader) para carregar. E assim, se o carregador de classe definido pelo usuário não conseguir encontrar esta classe, o programa lançará um ClassNotFoundError.
Todo o processo de carregamento é mostrado da seguinte maneira:
(A imagem é citada em: https://www.cnblogs.com/xing901022/p/4574961.html)
O rastreamento do código -fonte da fonte de carregamento da classe é o seguinte (o código -fonte foi apropriadamente simplificado aqui). Os leitores podem clicar no código -fonte para visualizar:
pacote java.lang.classloader; importação ...... classe protegida <?> loadclass (nome da string, resolução booleana) lança classNotFoundException {Synchronized (getClassLoadingLock (nome)) {// primeiro, encontre na memória da máquina virtual se esta classe foi carregada ... o principal problema com lies de cache! ! ! Classe <?> C = findLoadLedClass (nome); if (c == null) {long t0 = System.nanotime (); tente {if (pai! } else {c = findBootStrapClassornull (nome); }} catch (classNotFoundException e) {// classNotfoundException arremessado se a classe não encontrada // da classe pai não null carregador} if (c == null) {// calendar o método FindClass implementado por este carregador para carregar c = findClass (nome); }} if (resolve) {resolveclass (c); } retornar c; }}Você pode apreciar completamente o processo de mecanismo de delegação dos pais no código -fonte, e as três frases mais importantes de código foram marcadas:
Se o usuário precisar de um carregador personalizado e carregar o arquivo de classe do caminho especificado, ele precisará herdar o carregador de classe e implementar o método FindClass (Nome da String). Como exemplo:
pacote com.linuxidc.utils; importar java.io.bytearrayoutputStream; importar java.io.fileInputStream; importar java.io.ioException; importar java.io.inputStream; classe pública serviceClassLoader estende a classe de classe {private string classPath; public serviceClassloader (string classPath) {this.classpath = classPath; } /*** Reescreva o método FindClass da classe pai. A classe de carga da classe pai chamará este método */ @Override Class protegida <?> FindClass (nome da string) lança classeNotFoundException {class <?> C = null; byte [] classdata = getClassData (nome); if (classdata! = null) {c = DefinClass (nome, classdata, 0, classdata.length); } else {lança novo classNotFoundException (); } retornar c; } // Leia o arquivo de classe através do fluxo de IO e converta -o em uma matriz de bytes privada byte [] getClassData (nome da string) {string path = classPath + "/" + name.replace ('.', '/') + ".Class"; InputStream isTream = null; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); tente {iStream = new FileInputStream (caminho); byte [] buffer = novo byte [1024]; int temp = 0; while ((temp = iStream.read (buffer))! =-1) {bytearrayoutputStream.write (buffer, 0, temp); } if (bytearrayoutputStream! = null) {return bytearrayoutputStream.tobytearray (); }} catch (Exceção e) {e.printStackTrace (); } finalmente {tente {if (iStream! = null) {iStream.close (); }} catch (ioexception e) {e.printStackTrace (); } tente {if (byteArrayOutputStream! = null) {bytearrayoutputStream.close (); }} catch (ioexception e) {e.printStackTrace (); }} retornar nulo; }}O código para usar o carregador de classe é o seguinte:
ServiceClassloader serviceClassloader = new ServiceClassLoader ("c:/myclass"); czlass <?> C = serviceClassloader.loadclass ("com.linuxidc.service.myclass");Se você usar o mesmo objeto ServiceClassLoader para carregar o mesmo arquivo de classe várias vezes, o objeto de classe após cada carga é o mesmo! No entanto, se o novo carregador de classe personalizado diferente carregar o mesmo arquivo de classe, um objeto de classe diferente será retornado a cada vez.
NOTA: O arquivo de classe que você deseja carregar não pode ser colocado no diretório do ClassPath e quaisquer subdiretos, caso contrário, será carregado primeiro pelo AppClassLoader (isso ocorre porque o carregamento da classe adota o mecanismo de delegação dos pais, e o AppClassLoader pode carregar todos os arquivos de classe no caminho de classe). Cada vez, o mesmo AppClassLoader é carregado, então haverá problemas de cache de classe.
Isso resolve o problema de usar diretamente a reflexão quando a classe JVM carrega.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.