首页 > 其他分享 >JVM专题学习之类加载器(二)

JVM专题学习之类加载器(二)

时间:2024-12-21 16:21:46浏览次数:11  
标签:null String loadClass 专题学习 JVM 加载 class name

类加载器

三层类加载器

1.启动类加载器-BootstrapClassLoader
AppClassLoader负责加载核心类,存放在lib目录下的jar包或class文件。
2.扩展类加载器-ExtensionClassLoader
ExtensionClassLoader负责加载\lib\ext目录下的jar包或class文件,我们可以将通用性的功能,打成jar包放置到ext
3.应用程序加载器-ApplicationClassLoader
ApplicationClassLoader负责加载用户类路径的所有类库,比如classpath就是由ApplicationClassLoader加载的。

他们三者关系如图所示:
img

双亲委派机制

工作机制

如果一个类加载器收到了类加载的请求,它会把这个请求委派给父类加载器去完成,每层的类加载都是这个处理逻辑,如果父加载器反馈自己无法完成的时候,子加载类就会自己去尝试加载。

为什么这样设计一个机制

其实是为了保证基础类库的稳定性,安全性。基础类rt.jar有很多我们开发中经常用到的类比如String类,假设没有设计这个机制,我们在项目中同样声明一个String类那岂不是被替换掉了。由此可以看出,此机制设计的目的在于保证基础类库的稳定性和安全性。

学习源码

//java.lang.ClassLoader
public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
 synchronized (getClassLoadingLock(name)) {
    // First, check if the class has already been loaded
    //1.首先,检查这个类是否被加载了
    Class<?> c = findLoadedClass(name);
     if (c == null) {
            //2.当前这个类未被加载
            //2.1 让上层去负责加载类
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    //如果存在上层的类加载器
                    //就交给父加载器去加载
                    c = parent.loadClass(name, false);
                } else {
                    //如果不存在上层的类加载器
                    //就交给BootstrapClassLoader来负责加载
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                //2.1 说明父加载器未加载到类
                long t1 = System.nanoTime();
                //交给自己来加载
                c = findClass(name);
                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
 }

自定义类加载器

1.代码构思

建好一个Order类,然后在Main方法中new一个Order类。在项目文件夹中找到Order.class文件。复制到这个路径:G:\com\classLoader\Order.class,然后删掉Order.java文件,因为项目中的类会优先使用ApplicationClassLoader去加载,达不到我们自定义类加载的效果。

  1. 继承java.lang.ClassLoader
  2. 重写findClass方法
  3. 自定义一个方法,通过自定义类加载器,加载磁盘上的class文件

注意:包名要保持一致,测试代码中的包名要和磁盘中的包名保持一致。要不然会报错

2.代码实现

package com.classLoader;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MyClassLoader extends ClassLoader{
    //指定从哪加载类
    private String classPath;

    public MyClassLoader(String classPath){
        this.classPath = classPath;
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        //1.根据类全名获取到对应的字节数组
        byte[] data = loadByte(className);
        //2.通过JDK提供的API,将字节数组转换为Classs对象
        return defineClass(className,data,0,data.length);

    }

    private byte[] loadByte(String className) {
        //1.将类全名转换为完整类路径
        className = className.replaceAll("\\.","/");
        //G:\javaHomeWork\jvmDemo\src\main\java\com\Demo\com.classLoader
        StringBuilder stringBuilder = new StringBuilder(classPath);
        stringBuilder.append(className);
        stringBuilder.append(".class");
        System.out.println(stringBuilder);
        //2.采用文件字节流读取文件内容,并将其转换为字节数组
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(stringBuilder.toString());
            //3.创建字节数组,用于存放文件内容
            byte[] data = new byte[inputStream.available()];
            inputStream.read(data);
            return data;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(inputStream!=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}

3.测试代码

package com.classLoader;

public class MyClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException {
        MyClassLoader myClassLoader = new MyClassLoader("G:/");
        Class<?> clazz = myClassLoader.loadClass("com.Person");
        //AppClassLoader MyClassLoader
        System.out.println(clazz.getClassLoader());
        System.out.println(myClassLoader.getParent());
    }
}

4. 运行结果

img

5. 自定义类加载器关系

img

6. 打破双亲委派机制

我们在不删除Order类的情况下去使用我们的自定义类加载器去加载Order类

思路如下:

  • loadClass方法规定了双亲委派机制的工作模式
  • 只需要重写loadClass方法

代码实现

@Override
    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) {
                if (name.startsWith("com.classLoader")) {
                    //如果是我们自己项目自定义的类,则交给自己来加载
                    c = findClass(name);
                }else{
                    //交给父类加载器去加载
                    //保证核心类库的唯一性
                    c = this.getParent().loadClass(name);
                }

            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

运行结果

img

7.Tomcat的类加载机制

tomcat如何保证每个应用的类库的独立的?

img
不同的类加载器实例加载的类是隔离的
tomcat为每个web项目创建一个类加载实例,webappClassLoader

标签:null,String,loadClass,专题学习,JVM,加载,class,name
From: https://www.cnblogs.com/codexj/p/18620866

相关文章

  • 49天精通Java(Day 30):Java的类加载机制
    ......
  • require如何同步加载模块?
    在前端开发中,require通常用于在Node.js环境中同步加载模块。然而,在浏览器环境中,原生的JavaScript并不支持require函数来同步加载模块。不过,你可以通过一些工具和库来实现在浏览器中的模块化加载。以下是一些常见的方法来实现前端开发中模块的同步加载:使用Browserify或......
  • 面试官最常问的几个JVM面试题,几乎必考
    此部分内容虽然并非每位面试官都会涉及,但对于应聘高级职位(高P)而言,这一环节至关重要。面试难度因人而异,有的面试官可能仅要求简述虚拟机的内存实现,而有的则可能深入要求解释垃圾回收机制、虚拟机调优实战经验及线上问题排查等。每当我看到简历上提及JVM(Java虚拟机),无论应聘者的......
  • 31.在 Vue 3 中使用 OpenLayers 加载 CSV 数据,显示各个点
    目录一、前言二、项目准备1.安装Vue32.安装OpenLayers和D3三、CSV数据格式四、实现步骤1.配置Vue3+OpenLayers项目结构Map.vue文件2.解析代码dataSource和map变量showPoints方法featureStyle方法initMap方法3.启动开发服务器五、总结......
  • Hive其三,数据库操作,小技巧设置,加载数据等操作
    目录一、操作数据库二、关于表的操作1)关于字符类型的2)创建表3)修改表4)删除表5)小案例演示三、Hive中经常使用的小技巧的设置四、加载数据1)加载本地数据:2)从HDFS加载到Hive中:3)将数据直接放入表对应的文件夹下4)从其他表中加载数据5)克隆表数据五、通过hive进行......
  • vb编译环境运行没问题,生成exe运行时报错,错误"48"加载dll错误,右键以管理员身份运行可以
    解决办法(推荐):打开vb后,弹出新建工程标准exe,要点打开。然后再打开已建的工程,这样生成的exe可以直接双击运行,就不会报错了。 2、如需重装vb,要记得“数据访问”点“更改选项”去掉ADO和RDS前面的勾选,不然会一直停在更新状态。1.打开安装包点击SETUP.EXE(如果会跳出一个兼容性......
  • JVM参数讲解
    JVM(JavaVirtualMachine)参数是用于配置Java程序运行时行为的重要工具。它们通常用于调整性能、调试、监控以及特定环境下的优化。JVM参数主要分为两类:启动参数(启动时传递给JVM)和运行时参数(JVM在运行过程中动态修改)。下面是一些常用的JVM参数及其使用场景:一、JVM启动参数......
  • Z-BlogPHP 后台 JavaScript 加载失败的原因是什么?
    “后台JavaScript加载失败”错误通常出现在Z-BlogPHP中,表示浏览器在加载后台页面时无法正确加载JavaScript文件。以下是常见的原因和解决方法:浏览器版本过低:使用老旧的浏览器版本(如IE6/7/8等)可能导致JavaScript加载失败。解决方法:更新浏览器到最新版本,建议使用现......
  • ArkWeb页面拦截与自定义响应 - 控制加载过程
    ArkWeb页面拦截与自定义响应-控制加载过程简介在Web应用开发中,有时我们需要对页面加载过程进行更精细的控制,比如拦截特定的请求并返回自定义的响应内容。ArkWeb框架提供了这样的能力,允许开发者拦截页面和资源加载请求,并自定义响应。本文将详细介绍如何使用ArkWeb框架实现这些......
  • antd使用Image加载图片失败后重试
    使用antdImage加载多张图片时,遇到网络之类的问题,导致图片加载失败,希望能重试3次,之后再展示默认图片代码如下:constretry={}consthandleError=(e,id,url)=>{letcur=retry[id]retry[id]=cur?++cur:1;if(retry[id]>=3){console.l......