【JVM】类加载系统与双亲委派机制

1、JVM介绍

1.1、JVM是什么

Java Virtual Machine(Java虚拟机)是java程序实现跨平台的⼀个重要的工具(部件)。

在这里插入图片描述

HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。

在这里插入图片描述

只要装有JVM的平台,都可以运行java程序。那么Java程序在JVM上是怎么被运行的?

通过介绍以下JVM的三个组成部分,就可以了解到JVM内部的⼯作机制

  • 类加载系统:负责完成类的加载
  • 运行时数据区:在运行Java程序的时候会产生的各种数据会保存在运行时数据区
  • 执行引擎:执行具体的指令(代码)

1.2、学习的目的

我们学JVM相关知识的目的是为了充分理解jvm内部的工作流程,来掌握如何通过相应的参数配置,实现JVM的调优。

2、类加载系统

2.1、类的加载过程

⼀个类被加载进JVM中要经历哪几个过程

  • 加载: 通过io流的方式把字节码文件读入到jvm中(方法区)
  • 校验:通过校验字节码文件的头8位的16进制是否是java魔数cafebabe
  • 准备:为类中的静态部分开辟空间并赋初始化值
  • 解析:将符号引用转换成直接引用。——静态链接
  • 初始化:为类中的静态部分赋指定值并执⾏静态代码块。

类被加载后,类中的类型信息、方法信息、属性信息、运行时常量池、类加载器的引用等信息会被加载到元空间中。

2.2、类加载器

类是谁来负载加载的?——类加载器

  • Bootstrap ClassLoader 启动类加载器:负载加载jre/lib下的核心类库中的类,⽐如rt.jar、charsets.jar

  • ExtClassLoader 扩展类加载器:负载加载jre/lib下的ext⽬录内的类

ext 加载路径:System.getProperty("java.ext.dirs");
  • AppClassLoader 应⽤类加载器:负载加载⽤户⾃⼰写的类
app 加载路径:System.getProperty("java.class.path");
  • ⾃定义类加载器:⾃⼰定义的类加载器,可以打破双亲委派机制。

在这里插入图片描述

3、双亲委派机制

3.1、双亲委派机制介绍

当类加载器进⾏加载类的时候,类的加载需要向上委托给上⼀级的类加载器,上⼀级继续向上委托,直到启动类加载器。启动类加载器去核心类库中找,如果没有该类则向下委派,由下⼀级扩展类加载器去扩展类库中,如果也没有继续向下委派,直到找不到为止,则报类找不到的异常。

public class TestJMM {
    //静态属性
    public static int baseData = 10;
    //静态属性
    public static Student student = new Student();

    public static String hello = "hello";
}

应⽤类加载器怎么加载Student和String呢?需要通过双亲委派机制

在这里插入图片描述

3.2、为什么要有双亲委派机制

  • 防⽌核心类库中的类被随意篡改
  • 防⽌类的重复加载

3.3、双亲委派机制核心源码

ClassLoader.class

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;
    }
}

3.4、全盘委托机制

当⼀个类被当前的ClassLoader加载时,该类中的其他类也会被当前该ClassLoader加载。除非指明其他由其他类加载器加载。

3.5、自定义类加载器实现双亲委派机制

  • 自定义类加载机制
package com.qf.jvm;

import java.io.FileInputStream;
import java.lang.String;

/**
 * @author Thor
 * @公众号 Java架构栈
 */
public class MyClassLoader extends ClassLoader{

    private String classPath;

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


    @Override
    protected Class<?> findClass(final String name)
            throws ClassNotFoundException
    {
        try {
            //1.读入指定路径的classPath下的类
            String path = name.replace('.', '/').concat(".class");
            FileInputStream fileInputStream = new FileInputStream(classPath+"/"+path);
            byte[] data = new byte[fileInputStream.available()];
            fileInputStream.read(data);
            fileInputStream.close();

            //2.加载该类
            return defineClass(name, data,0,data.length);

        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }
}

  • 启动自定义类加载器
package com.qf.jvm;

import java.lang.reflect.Method;
import java.lang.String;

/**
 * @author Thor
 * @公众号 Java架构栈
 */
public class TestMyClassLoader {

    public static void main(String[] args) throws Exception {
        //1.创建自定义类加载器,指定加载路径
        MyClassLoader myClassLoader = new MyClassLoader("C:\\note\\java\\JVM-2021-千锋\\文档及代码\\my_class");
        //2.指定要加载的类名
        Class<?> clazz = myClassLoader.loadClass("com.qf.jvm.JVMAnalyze");
        //3.反射创建对象
        Object obj = clazz.newInstance();
        //4.创建add方法对象
        Method add = clazz.getDeclaredMethod("add", null);
        //5.调用对象的add方法
        Object result = add.invoke(obj);
        //6.打印结果
        System.out.println(result);
    }
}

3.6、自定义类加载器打破双亲委派机制

/**
 重写loadClass⽅法
 */
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 still not found, then invoke findClass in order
            // to find the class.
            long t1 = System.nanoTime();
            //对于Object类,使用父加载器
            if(!name.startsWith("com.qf.jvm")){
                c = this.getParent().loadClass(name);
            }else{
                c = findClass(name);
            }

            // this is the defining class loader; record the stats
            sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
            sun.misc.PerfCounter.getFindClasses().increment();

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

本文章参考B站 千锋教育JVM全套教程(含jvm调优、jvm虚拟机、jvm面试题、jvm源码详解)系统玩转java虚拟机全程干货无废话,仅供个人学习使用,部分内容为本人自己见解,与千锋教育无关。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值