【JVM】类的加载机制

197 阅读2分钟

这是我参与更文挑战的第9天,活动详情查看: 更文挑战

类加载机制

JVM使用类的流程是这样的: java文件->编译->.class文件,类加载器通过加载.class文件这样就创建了一个Class类的实例,通过这个实例我们可以完成创建对象,调用方法一系列操作。

首先需要了解下三种类加载器

类加载器

虚拟机设计团队把加载动作放到 JVM 外部实现,以便让应用程序决定如何获取所需的类, JVM 提 供了 3 种类加载器:

启动类加载器(Bootstrap ClassLoader)

  1. 负责加载 JAVA_HOME\lib 目录中的, 或通过-Xbootclasspath 参数指定路径中的, 且被 虚拟机认可(按文件名识别, 如 rt.jar) 的类。
  • 这个类加载器使用C/C++语言实现的,嵌套在JVM内部
  • 并不继承自java.lang.ClassLoader
  • 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类

扩展类加载器(Extension ClassLoader)

  1. 负责加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类 库。

应用程序类加载器(Application ClassLoader):

  1. 负责加载用户路径(classpath)上的类库。

JVM 通过双亲委派模型进行类的加载, 当然我们也可以通过继承 java.lang.ClassLoader 实现自定义的类加载器。

双亲委派机制

当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。

采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。

image.png

代码

public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println(java.lang.String.class.getClassLoader());  //null
        System.out.println("-------------------");
        System.out.println(Student1.class.getClassLoader());//sun.misc.Launcher$AppClassLoader@58644d46v
        System.out.println("-------------------");
        //DNSNameService类位于dnsns.jar包中,它存在于jre/lib/ext目录下
        System.out.println(DNSNameService.class.getClassLoader());//sun.misc.Launcher$ExtClassLoader@45ee12a7
    }
}

class Student1 {

}

BootstrapClassLoader对Java不可见,所以返回了null