JVM——类加载器

1 定义

顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中【只有加载到JVM中,JAVA程序才能使用该类】。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码存入到JVM的方法区中。并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 **newInstance()**方法就可以创建出该类的一个对象,存储在JVM中的堆空间处,封装该类在方法区中所有数据结构信息

从加载到JVM的过程中,描述了类的生命周期。
JVM加载类

类加载过程

2 类加载器类型

Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:

  1. 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。加载路径为
  2. 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  3. 系统类加载器(app class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
    除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求

类加载的加载顺序为BootsTrap Classloader>Extensions Classloader > AppClassloader
且每个加载器都有父加载器

public class Test {

    public static void main(String[] args) {

       
        ClassLoader loader=Test.class.getClassLoader();
        //sun.misc.Launcher$AppClassLoader@b4aac2 Test类的类加载器为AppClassLoader
        System.out.println(loader);

        //父加载器  sun.misc.Launcher$ExtClassLoader@154617c Test类的类加载器的父加载器为ExtClassLoader
        System.out.println("Test parent》"+loader.getParent());
        //父级的父级加载器 BootTrapClassLoader  打印为null ,是由c++实现的 是JVM的一部分,所以在JAVA代码中无法获得相关的引用
        System.out.println("Test parent' parent 》"+loader.getParent().getParent());

        ClassLoader loader1=String.class.getClassLoader();
        //String等基础类是由BootStrap类加载器进行加载
        //BootStrap类加载器 是由c++实现的 是JVM的一部分,所以在JAVA代码中无法获得相关的引用
        System.out.println("String  parent》");


        
        //C:\Program Files (x86)\Java\jdk1.8.0_171\jre\lib\resources.jar;C:\Program Files (x86)\Java\jdk1.8.0_171\jre\lib\rt.jar;
        //C:\Program Files (x86)\Java\jdk1.8.0_171\jre\lib\sunrsasign.jar;C:\Program Files (x86)\Java\jdk1.8.0_171\jre\lib\jsse.jar;
        //C:\Program Files (x86)\Java\jdk1.8.0_171\jre\lib\jce.jar;C:\Program Files (x86)\Java\jdk1.8.0_171\jre\lib\charsets.jar;
        //C:\Program Files (x86)\Java\jdk1.8.0_171\jre\lib\jfr.jar;C:\Program Files (x86)\Java\jdk1.8.0_171\jre\classes
        System.out.println("BootTrap加载目录"+System.getProperty("sun.boot.class.path")+"\n");
        //当前加载路径可以使用指令进行切换 -D java.ext.dirs
        //C:\Program Files (x86)\Java\jdk1.8.0_171\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
        System.out.println("Extension ClassLoader加载目录"+System.getProperty("java.ext.dirs")+"\n");
        System.out.println("AppClassLoader加载目录"+System.getProperty("java.class.path")+"\n");
    }
}

3双亲委派机制

类加载的过程是通过**“委托”**机制实现的。
1:请求AppClassLoader 类加载器进行加载,判断是否有缓存,存在的话结束加载过程
2:1步骤时,无缓存的class信息,向上委托ExtClassLoader进行加载,判断是否有缓存,存在的话结束加载过程,否则进行步骤三
3:2步骤时,无缓存的class信息,向上委托Bootstrap进行加载,判断是否有缓存,存在的话结束加载过程,否则进行步骤四
4:3步骤时,无缓存信息,去BootstrapClassLoader规定的路径中进行加载,存在时结束加载过程,并在缓存中存储类的缓存信息,否则向下进行委托查找
5:4步骤时,未加载到相应的类信息,去ExtClassLoader规定的路径中进行加载,存在时结束加载过程,并在缓存中存储类的缓存信息,否则向下进行委托查找
6:5步骤时,未加载到相应的类信息,去AppClassLoader 规定的路径中进行加载,并在缓存中存储类的缓存信息

总结:

1每个类加载器对他加载过的类都有一个缓存,
2 向上委托查找,向下委托加载

双亲委派

4自定义加载器

- 编写一个类继承自ClassLoader类
- 复写它的findClass()方法
- findClass()方法中调用defineClass

//自定义类加载器用于加载Classe二进制文件

public class MyClassLoader extends ClassLoader {
    private  String classPath;
    public MyClassLoader(String classPath){
        this.classPath=classPath;
    }
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        //需要加載的全路徑
        classPath=classPath+name.replace(".","\\").concat(".class");
        File file=new File(classPath);
        //文件流中读取的二进制数据
        byte[] t;
        int length=0;
        try {
            FileInputStream fis=new FileInputStream(file);
            ByteArrayOutputStream fos=new ByteArrayOutputStream();
            while ((length=fis.read())!=-1){
                fos.write(length);
            }
            t=fos.toByteArray();
            return defineClass(name,t,0,t.length);
        } catch (Exception e)
        {
            e.printStackTrace();
            throw new  ClassNotFoundException("类文件信息错误");

        }
    }
}

被加载的类信息【需要提前编译成为class文件】

package classLoader;

/**
 * @author lyf
 * @projectName Highly_Concurrent_Programming
 * @date 2022/2/15 下午 03:32
 * @description
 */
public class SalaryCalculator {

    public  double cal(double salary){
        return salary*1.5;
    }
}

测试类加载Class文件调用cal方法

public class SalaryCalculatorMain {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

         //被加载类的class路径
         String classPath="D:\\LYF\\Highly_Concurrent_Programming\\JavaPractice\\Thread\\target\\classes\\";

         MyClassLoader loader=new MyClassLoader(classPath);
         //根據class的名字加載class二進制數據
         //被加载的类路径名classLoader.SalaryCalculator
         Class<?> salaryCalculator = loader.loadClass("classLoader.SalaryCalculator");
         //获得实列
         Object o = salaryCalculator.newInstance();
         double salary=1000;
         //获得类中的cal(double s)方法
         Method cal = salaryCalculator.getMethod("cal",double.class);
         //执行该实例的cal方法
         Object invoke = cal.invoke(o, salary);
         System.out.println("工資為"+(Double) invoke); //工資為1500.0
    }
}

5 热加载

对jvm方法区中类定义进行替换,因为堆(heap)中的Class对象是对方法区对象的封装,所以可以理解为对Class对象的替换,
当一个class被替换后,系统无需重启,替换的类会立即生效。

实现方法:创建类实例时,需要重复地创建该类的类加载器【因为每个类在加载时,由于缓存的存在,仅仅只会加载一次】,从而达到实时更新class信息

缺点:类加载器的频繁创建,占用jvm的内存空间
忽略类加载过程中的Linking步骤,会使得本应该在编译阶段抛出的异常延时抛出

    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();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        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.
                    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;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值