美文网首页java
java ClassLoader机制

java ClassLoader机制

作者: spraysss | 来源:发表于2019-10-27 15:53 被阅读0次

java 编译后的class文件在需要使用时会由ClassLoader加载到内存中,每一类的所有实例都只有一个唯一的Class对象
关于Class对象应该放在jvm的哪个内存空间中JVM规范并没有明确的规定,HotSpot虚拟机将其放在方法区中

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
        System.out.println(a1.getClass() == a2.getClass());//true

    }
    static class A {

    }
}

ClassLoader

  • 每一个Class对象都有一个引用指向定义它的ClassLoader
  • jvm类加载机制为父委托机制,即需要加载一个类时,会先看其父加载器是否可以加载该类,如果可以加载则由父加载器加载

JVM自带3种ClassLoader,用于加载class

  • Bootstrap ClassLoader 用于加载java核心类库,比如java.lang.*,它处于最上层,没有父加载器
  • Extension ClassLoader 的父加载器为Bootstrap ClassLoader
  • App ClassLoader 的父加载器为Extension ClassLoader,用于加载我们自己写的类
classloader

类加载的过程

loading

使用ClassLoader加载类到JVM内存

linking

  • verification 合法性验证
  • preparation 为类的静态变量分配内存空间并将其初始化为默认值
  • resolution 把类中的符号引用转为直接引用

initialization

为类的静态变量赋正确的初始值

主动使用VS被动使用

JVM只有在首次主动使用类或者接口时才会去初始化该类或接口的Class对象
被动使用只会做加载动作,不会做初始化动作
jvm参数 -XX:+TraceClassLoading 可以查看类加载详细信息

几个典型被动使用的例子

  1. 子类调用父类的静态字段
  2. 调用final 修饰的静态字段(这些字段会存放在调用类的常量池中)
  3. new 类的数组
查看Bootstrap ClassLoader 加载的内容
public class Test {
    public static void main(String[] args) {
        Arrays.stream(System.getProperty("sun.boot.class.path").split(":")).forEach(System.out::println);
    }

}

output

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/resources.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/sunrsasign.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jsse.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jce.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/charsets.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jfr.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/classes

查看Extension ClassLoader 加载的内容

public class Test {
    public static void main(String[] args) {
        Arrays.stream(System.getProperty("java.ext.dirs").split(":")).forEach(System.out::println);
    }

}

输出

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext
/Library/Java/Extensions
/Network/Library/Java/Extensions
/System/Library/Java/Extensions
/usr/lib/java

App Classloader

public class Test {
    public static void main(String[] args) {
        System.out.println(new A().getClass().getClassLoader());
        //sun.misc.Launcher$AppClassLoader@18b4aac2

    }
    static  class A{

    }

}

输出

     sun.misc.Launcher$AppClassLoader@18b4aac2

自定义类加载器

自定义类加载器需要继承抽象类ClassLoader并重写findClass方法

  • findClass 用于将指定路径下的class文件转化为Class对象,内部调用 defineClass
  • loadClass ClassLoader用于加载类的外部接口

具体步骤:

  1. 首先创建存放class文件的目录
mkdir -p /tmp/test/com/yz/clz
cd /tmp/test/com/yz/clz
  1. 编写A类
package com.yz.clz;

public class A {
    public void helloWorld() {
        System.out.println("Hello World");
    }
}
  1. javac 编译生产class文件
 javac A.java 
  1. ls 可以看的A.java生成的A.class文件
A.class A.java
  1. 自定义ClassLoader加载A.class
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;

public class MyClassLoader extends ClassLoader {
    private String dir;

    public MyClassLoader(ClassLoader parent, String dir) {
        super(parent);
        this.dir = dir;
    }

    public MyClassLoader(String dir) {
        this.dir = dir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String cp = name.replace(".", "/");
        File classFile = new File(dir, cp + ".class");
        if (!classFile.exists()) {
            throw new ClassNotFoundException("class " + name + "not find");
        }


        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             FileInputStream fis = new FileInputStream(classFile)) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            byte[] classBytes = baos.toByteArray();
            return defineClass(name, classBytes, 0, classBytes.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;

    }

    public static void main(String[] args) throws Exception {
        ClassLoader classLoader = new MyClassLoader("/tmp/test");
        Class<?> aClz = classLoader.loadClass("com.yz.clz.A");
        System.out.println(aClz.getClassLoader());
        Method method = aClz.getMethod("helloWorld");
        Object obj = aClz.newInstance();
        method.invoke(obj);
    }
}
  1. 测试输出如下结果
com.yz.clz.MyClassLoader@1376c05c
Hello World

一些比较好的类加载文章

相关文章

网友评论

    本文标题:java ClassLoader机制

    本文链接:https://www.haomeiwen.com/subject/imcuvctx.html