美文网首页
Jvm类加载器

Jvm类加载器

作者: 程序设计法师 | 来源:发表于2018-12-27 16:25 被阅读0次

Jvm提供了三大内置的类加载器,不同的类加载器负责将不同的类加载到内存之中

  1. 根加载器(Bootstrap ClassLoader)
    是最顶层的加载器,是由C++编写的,主要负责虚拟机核心类库的加载,如整个java.lang包,根加载器是获取不到引用的,因此String.class.getClassLoader()返回为空
  2. 扩展类加载器(Ext ClassLoader)
    他是根加载器的子类,由纯java实现
  3. 系统类加载器(Application ClassLoader)
    负责加载classpath下的类库资源,或者我们项目开发中的第三方jar,他的父加载器是扩展类加载器,同时他也是自定义类加载器的默认父加载器
    然后我们自定义个类加载器
@RequiresApi(api = Build.VERSION_CODES.O)
public class MyClassLoader extends ClassLoader {
    //定义默认的class存放路径
    private final static Path DEFAULT_CLASS_DIR= Paths.get("E:","classex");
    private final Path classDir;
    public MyClassLoader(){
        super();
        classDir = DEFAULT_CLASS_DIR;
    }
    //允许放入指定路径的class路径
    public MyClassLoader(String classDir){
        super();
        this.classDir=Paths.get(classDir);
    }
    //指定class路径的同时,指定父加载器
    public MyClassLoader(String classDir,ClassLoader parent){
        super(parent);
        this.classDir=Paths.get(classDir);
    }
    //重写父类的findClass方法

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //读取class的二进制数据
        byte[] classBytes=this.readClassBytes(name);
        if(null==classBytes||classBytes.length==0){
            throw new ClassNotFoundException("Can not load the class "+name);
        }
        //调用defineClass方法定义class
        return this.defineClass(name,classBytes,0,classBytes.length);
    }
    //将class文件读入内存
    private byte[] readClassBytes(String name) throws ClassNotFoundException {
        //将包名分隔符转换为文件路径分隔符
        String classPath=name.replace(".","/");
        Path classFullPath=classDir.resolve(Paths.get(classPath+".class"));
//        Path classFullPath=Paths.get(classPath+".class");
        if(!classFullPath.toFile().exists()){
            throw new ClassNotFoundException("The class "+name+"not found");
        }
        try(ByteArrayOutputStream baos=new ByteArrayOutputStream()){
            Files.copy(classFullPath,baos);
            return baos.toByteArray();
        } catch (IOException e) {
            throw new ClassNotFoundException("load the class "+name + "occurerror.",e);
        }
    }
    public String toString(){
        return "My ClassLoader";
    }
}
package classloader;

public class World {
    public String welcome(){
        return "你好";
    }
}
public class TestClassLoader {
    @RequiresApi(api = Build.VERSION_CODES.O)
    public static void main(String [] args){
        MyClassLoader myClassLoader=new MyClassLoader();
        try {
            Class<?> aclass=myClassLoader.loadClass("classloader.World");
            System.out.println(aclass.getClassLoader());
            Object helloWorld=aclass.newInstance();
            System.out.println(helloWorld);
            Method welcomeMethod=aclass.getMethod("welcome");
            String result= (String) welcomeMethod.invoke(helloWorld);
            System.out.print("Result:"+result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
sun.misc.Launcher$AppClassLoader@232204a1
classloader.World@1540e19d
Result:你好

双亲委托机制

当一个类加载器被调用了loadClass之后他并不会直接将其加载,而是先交给父加载器尝试加载直到最顶层的父加载器,然后再依次向下加载,直到加载成功
那么我们如何绕过双亲委托机制,直接使用我们自定义的类加载器呢

  1. 绕过系统加载器,直接将扩展类加载器作为MyClassLoader的父加载器
  //获取系统类加载器
    ClassLoader extClassLoader=TestClassLoader.class.getClassLoader().getParent();
    MyClassLoader classLoader=new MyClassLoader("G:\\classloader",extClassLoader);
    Class<?> mClass=classLoader.loadClass("com.classloader.World");
    System.out.println(classLoader);

这样一来,根加载器和扩展类加载器都无法对该类加载,自然而然只能交给MyClassLoader进行加载

  1. 在构造MyClassLoader 的时候指定其父类加载器为null
  //获取系统类加载器
    MyClassLoader classLoader=new MyClassLoader("G:\\classloader",null);
    Class<?> mClass=classLoader.loadClass("com.classloader.World");
    System.out.println(classLoader);

使用不同的类加载器,或者同一个类加载器的不同实例,去加载同一个class,则会在堆内存和方法区产生多个class对象

在类加载器进行类加载的时候,首先会到加载记录表也就是缓存中,查看该类是否已经被加载过了,如果已经被加载过了,就不会重复加载,否则将会认为其是首次加载

JVM规定了一个Class只有在满足下面三个条件的时候才会被GC回收,也就是类被卸载:

  1. 该类的所有的实例都已经被GC,比如Simple.class的所有Simple实例都被回收掉
  2. 加载该类的ClassLoader实例被回收
  3. 该类的class实例没有在其他地方被引用

相关文章

网友评论

      本文标题:Jvm类加载器

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