• 面试刷题23:类加载过程和双亲委派机制?


    image.png




    jvm赋能java跨平台的能力,而类加载机制是深入理解java的必要条件。

    我是李福春,我在准备面试,今天的问题是:

    java的类加载机制是怎样的?什么是双亲委派原则?

    答:java的类加载过程分为 加载,链接,初始化。
    加载:即从数据源(jar,class,网络)加载class文件到jvm,映射为class对象,如果不是classFile结构,抛出ClassFormatError;
    链接:把第一步得到的class对象转换到jvm环境,进行验证(字节信息是否符合jvm规范,否则抛出VerifyError),准备(为静态变量分配内存空间),解析(常量池中的符号引用替换为直接引用);
    初始化:即为静态变量和静态代码块赋值。

    java的类加载器部分的加载器分4类,见上图。
    BootStrapClassLoader:加载jre/lib下的jdk的jar包,具有超级权限,是最顶级的类加载器;
    ExtensionClassLoader:加载jre/lib/ext下的jar包,加载jdk的扩展程序包;
    ApplicationClassLoader:加载当前应用classpath下的jar包或者class文件;
    自定义ClassLoader:用户自定义的类加载器,一般是为了进行进程隔离,或者自己操纵字节码;

    双亲委派机制:即为了避免类信息被重复加载和程序的安全性,父加载器优先子加载器加载类型到jvm,子加载器无法加载父加载器已经加载到jvm中的类型信息。


    下面做一下扩展:针对面试官可能会追问的细节。

    类加载器体系

    image.png



    指定超级加载器的目录和时机: ```java # 指定新的bootclasspath,替换java.*包的内部实现 java -Xbootclasspath: your_App

    a意味着append,将指定目录添加到bootclasspath后面

    java -Xbootclasspath/a:<your_dir> your_App

    p意味着prepend,将指定目录添加到bootclasspath前面

    java -Xbootclasspath/p:<your_dir> your_App

    
    <br />
    <br />指定扩展加载器的目录:
    ```java
    java -Djava.ext.dirs=your_ext_dir HelloWorld
    


    指定系统加载器的实现类:

    java -Djava.system.class.loader=com.yourcorp.YourClassLoader HelloWorld
    


    打印出类加载器: ```java package org.example.mianshi.classloader;

    import java.util.Collection;

    /**

    • 作者: carter
    • 创建日期: 2020/3/31 12:41
    • 描述: 类加载器的层级关系
      */

    public class PrintClassLoaderApp {

    public static void main(String[] args) {
    
        System.out.println("PrintClassLoaderApp 的类加载器是:"+PrintClassLoaderApp.class.getClassLoader());
        System.out.println("parent 的类加载器是:"+ PrintClassLoaderApp.class.getClassLoader().getParent());
        System.out.println("parent.parent 的类加载器是:"+ PrintClassLoaderApp.class.getClassLoader().getParent().getParent());
        System.out.println("Collection 的类加载器是:"+ Collection.class.getClassLoader());
    
    }
    

    }

    
    <br />输出结果:<br />我使用的java10,PlatformClassLoader替代了ExtensionClassLoader;
    ```java
    PrintClassLoaderApp 的类加载器是:jdk.internal.loader.ClassLoaders$AppClassLoader@4459eb14
    parent 的类加载器是:jdk.internal.loader.ClassLoaders$PlatformClassLoader@2ac1fdc4
    parent.parent 的类加载器是:null
    Collection 的类加载器是:null
    

    自定义类加载器

    自定义类加载器一般用来进行进程内隔离,或者需要自己操纵字节码的场景。


    过程如下:
    1,通过名称找到二进制代码,即class文件;
    2,使用class文件创建对应的class对象;


    示例代码如下:

    package org.example.mianshi.classloader;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * 作者:     carter
     * 创建日期:  2020/3/31 14:24
     * 描述:     自定义类加载器
     */
    
    public class CustomerClassLoaderApp extends ClassLoader {
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
    
            byte[] bytes = loadClassFromFile(name);
    
            return defineClass(name, bytes, 0, bytes.length);
        }
    
        private byte[] loadClassFromFile(String name) {
    
            InputStream inputStream = getClass().getClassLoader()
                    .getResourceAsStream(name.replace(".", File.separator) + ".class");
    
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int nextValue = 0;
            try {
                while ((nextValue = inputStream.read()) != -1) {
                    byteArrayOutputStream.write(nextValue);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            byte[] buffer = byteArrayOutputStream.toByteArray();
    
    
            return buffer;
        }
    }
    


    如何加速类加载


    1,   AOT , 即提前把字节码编译成机器码,然后在启动的时候指定机器码的位置,


    2,AppCDS ,即提前把类信息加载成为元数据,使用内存映射技术,免除类加载和解析的开销。


    小结


    本节回顾了jvm的类加载过程,类加载器的层次,双亲委派原则,


    然后指明了自定义类加载器的使用场景和基本过程,以及给了一个简单的例子;


    最后给出了两种加速类加载器速度的方法。





    image.png

    原创不易,转载请注明出处。

  • 相关阅读:
    Linux基础巩固--Day4--文本处理
    Linux基础巩固--Day3--用户组及权限操作
    2020撸python--argparse列出D盘目录详情
    2020撸python--socket编程
    Linux基础巩固--Day2--文件操作
    Linux基础巩固--Day1--背景介绍
    Let's Go -- 初始go语言
    ValueError: Related model 'users.UserProfile' cannot be resolved
    半虚拟化驱动virtio-Windows
    virt-install 创建虚拟机
  • 原文地址:https://www.cnblogs.com/snidget/p/12605134.html
Copyright © 2020-2023  润新知