java

java ClassLoader机制

2019-10-27  本文已影响0人  spraysss

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

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

classloader

类加载的过程

loading

使用ClassLoader加载类到JVM内存

linking

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方法

具体步骤:

  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

一些比较好的类加载文章

上一篇 下一篇

猜你喜欢

热点阅读