zoukankan      html  css  js  c++  java
  • jvm:类加载器

    1、类加载器

    (1)书写代码:

      public static void main(String[] args) {
          ClassLoader classLoade=Test1.class.getClassLoader();
          System.out.println(classLoade);
        }
    sun.misc.Launcher$AppClassLoader@18b4aac2

    (2)查看源码(sun.misc.Launcher):

    public Launcher() {
            Launcher.ExtClassLoader var1;
            try {
                var1 = Launcher.ExtClassLoader.getExtClassLoader();
            } catch (IOException var10) {
                throw new InternalError("Could not create extension class loader", var10);
            }
    
            try {
                this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
            } catch (IOException var9) {
                throw new InternalError("Could not create application class loader", var9);
            }

    执行代码:

        public static void main(String[] args) {
          ClassLoader classLoade=Test1.class.getClassLoader();
          while(classLoade!=null){
              System.out.println(classLoade.getParent());
          } 
        }
    sun.misc.Launcher$ExtClassLoader@1b6d3586

    所以,AppClassLoader的父类是ExtClassLoader,

    (3)查看源码(ClassLoader):

    protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    synchronized (getClassLoadingLock(name)) {
    // First, check if the class has already been loaded
    Class<?> c = findLoadedClass(name);
    if (c == null) {
    long t0 = System.nanoTime();

    锁保证了同时只能有一个可以LoadClass,同时保证了唯一性,首先检查是否被加载过了,如加载过了则直接返回(c!=null)。

                       if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);
                        }

    ExtClassLoader的父类是Bootstrap

    启动(Bootstrap)类加载器:用来加载java核心类库,无法被java程序直接引用。负责将JAVAHOME/lib下面的类库加载到内存中(比如rt,jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

    标准扩展(Extension)类加载器:它用来加载Java的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载Java类。负责将JAVA_HOME/jre/lib/ext或者由系统变量java.ext.dirs指定位置中的类库加载到内存中

    应用程序(Application)类加载器:它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。由于这个类加载器是ClassLoader中的getSystemClassLoader)方法的返回值,因此一般称为系统(System)加载器

    加载顺序:自顶向下

    检测顺序:自底向上

    (4)双亲委任机制

    当一个类收到了类加载请求时,不会自己先去加载这个类,而是将其委派给父类,由父类去加载,如果此时父类不能加载,反馈给子类,由子类去完成类的加载。

    目的:安全

    父类能加载的就不给子类加载

    程序安全是JDK的事,文件安全是系统的事

    程序员写了不安全的程序,JDK有责任不让它运行

    (5)其它

    判断两个对象是不是同一个对象:使用的是不是同一个类加载器

    以下内容转载自:https://blog.csdn.net/u010312474/article/details/91046318

    一道面试题
    能不能自己写个类叫java.lang.System?

    答案:通常不可以,但可以采取另类方法达到这个需求。
    解释:为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。

    但是,我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。

    双亲委派模式优势

    避免重复加载 + 避免核心类篡改
    采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java
    API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。

    JVM预定义的三种类型类加载器:

    • 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将
      <Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

    • 标准扩展(Extension)类加载器:是由 Sun 的
      ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将<
      Java_Runtime_Home >/lib/ext或者由系统变量
      java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。

    • 系统(System)类加载器:是由 Sun 的
      AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。

    双亲委派模型工作工程:

    1.当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。

    2.当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap
    ClassLoader去完成。

    3.如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>lib中未找到所需类),就会让Extension ClassLoader尝试加载。

    4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。

    5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。
    在这里插入图片描述
    在这里插入图片描述

    这就涉及到了类的具体加载过程, 如下图, 类的加载过程被从左到右划分为 3 大阶段:

    1.装载 (Loading)
    该阶段负责找到待加载类的二进制 class 文件, 并把它以 bytecode 的形式装载到虚拟机。 在这个过程中, JVM 会给这个类分配一个基本的内存结构, 但是方法, 变量域, 和它引用到的其他类在这个阶段都还没有处理, 也就是说, 这个类在目前阶段还不可用
    2.链接 (Linking)
    这个步骤又可细分为3个阶段

    • 字节码验证
      验证字节码是否是一个正确,符合规范的类字节码
    • 类准备
      为这个类定义好必须的数据结构以表示成员变量域, 方法, 以及实现的接口等等
    • 解析
      把这个类锁引用的其他类全部加载进来 , 引用的方式有如下几种:
      •  继承
      •  实现接口
      •  域变量
      •  方法定义
      •  方法中定义的本地变量

    3.初始化(Initializing)
    执行类中定义的静态代码块, 初始化静态变量为默认值

    隐式加载 vs 显示加载
    从上文类加载的详细过程可以看出, 类有两种方式被加载进来

    1. 显式加载
      程序主动调用下列类型的方法去主动加载一个类
    • classloader.loadClass( className)
    • Class.forName( className)
    1. 隐式加载
      被显式加载的类对其他类可能存在如下引用:
      • 继承
      • 实现接口
      • 域变量
      • 方法定义
      • 方法中定义的本地变量
      • 被引用的类会被动地一并加载至虚拟机, 这种加载方式属于隐式加载
  • 相关阅读:
    排序三 直接插入排序
    编写你的第一个django应用程序2
    编写你的第一个web应用程序1
    你被体制化了吗
    服务器安装tensorflow导入模块报错Illegal instruction (core dumped)
    查看数据库里有没有数据
    python实现贪吃蛇
    在pycharm中执行脚本没有报错但输出显示Redirection is not supported.
    linux安装redis
    pycharm快捷键
  • 原文地址:https://www.cnblogs.com/zhai1997/p/12595427.html
Copyright © 2011-2022 走看看