类加载过程(双亲委派模型)JVM内存模型以及垃圾回收机制算法(一)

本文我们将讲解JVM的内存模型和垃圾回收算法,由于内容较多,我们分为两篇文章进行讲解,其中,第一篇文章将讲解JVM内存模型及类加载器。第二篇文章讲解垃圾回收算法,相当于将一个对象从产生、使用到销毁的全流程全部进行解析。

  1. jvm类加载过程
    在这里插入图片描述
    从上图可以看出,就是我们一个java文件的加载过程,从最初始状态的java文件,通过javac命令编译转化成class文件,我们的jvm只是识别class文件,class文件通过ClassLoader类加载器加载对象,当然我们的代码里面可能引入的java类库中的包,也会将java类库加载进来,通过类加载器,字节码解释器和JIT编译器进行字节码文件(class文件)的解释和编译,然后交给执行引擎执行,执行引擎调用的是操作系统或者硬件设施,这样一个java文件就加载完毕了,而绿框框起来的范围就是jvm,java虚拟机。我们的jvm默认版本是HotSpot,当然还有其他的版本,比如openjdk,淘宝jdk等,至于如何查看jvm的版本,如下图:
    在这里插入图片描述

  2. ClassLoader类加载器类加载过程
    在这里插入图片描述
    上图就是ClassLoader的类加载全流程,也就是Class文件在类加载器中是如何运行的,第一步加载class文件,检查class文件的格式是否正确,准备创建对象,解析读取class文件的内容,根据内容来初始化对象,比如我们现在new一个对象,在new对象之前,类加载器就会执行以上流程,至于解析class文件,主要是class文件里面放了我们对象的一些属性和方法,比如有几个属性,属性名是啥,几个方法等,然后才会初始化对象。

  3. 双亲委派模型
    其实,在类加载的过程中,也就是初始化之前,还会有一个双亲委派机制,如下图:
    在这里插入图片描述

比如,我们现在初始化User对象,也就是在new User()初始化之前,类加载器会做上图的行为,我们称为双亲委派机制,那么,双亲委派机制到底是干什么的,为什么要这样做,我来一一解释。
首先,我们初始化对象的时候,也就是在new User()的时候,会先获取当前User类的包名加类名全路径,因为这个代码是我们自己写的,所以存在于应用类加载器中,也就是说,初始化的时候我们用的类加载器是应用类加载器,然后应用类加载器会去查找自己的父类,也就是扩展类加载器,扩展类加载器会继续寻找自己的父类,即启动类加载器,启动类加载器没有父类了,则启动类加载器就是最高级别的加载器,这个时候,启动类加载器会去当前jdk的安装目录下的jre/lib这个目录下去查找存不存在一个类是User,判断的依据是包名加类名都是相同的,我们知道,lib包下存放的都是java类库里面提供的类,比如java.lang包,java.util包等,如果找不到,则会继续向下去应用类加载器的默认扫描路径即/jre/lib/ext目录下查找是否存在当前类,如果不存在则继续向下找到应用类加载器去查找,当然应用类加载器是肯定能找到当前类的,这个时候,就会去初始化对象,全流程就是上图所写先从下往上寻找类加载器,然后再从最顶层依次往下寻找类,这就是双亲委派模型。
那么,为什么要这样做呢?
这样做的目的主要是为了防止我们自己写的代码,创建的包名加类名和java类库中提供给我们的类重复,比如有人自己创建包名为java.lang,然后在该包下创建类Object,就和我们的类库中的重复了,那么此时类加载器会这样处理,先依次从下往上找到启动类加载器,然后开始从上往下扫描类,但是启动类加载器在/jre/lib包下扫描到了java.lang.Object该类,和我们创建的类的全路径完全一致,此时启动类加载器就会停止扫描,不会再继续去执行扩展类加载器和应用类加载器了,那么我们初始化的对象就会被当做是java提供给我们的Object类,而不是我们自定义的Object类,这样就可以防止类冲突。
综上:双亲委派模型存在的意思就是为了防止用户自定义类与java类库中提供的类冲突而提出来的一种实现思路,该方法在源码中是通过一个方法实现的,该方法名为loadClass,是在我们初始化对象之前执行的。
我截图给大家,该方法逻辑就是,先获取当前的类加载器,再依次往上查找Parent类加载器,获取到类加载器之后再依次往下扫描类:
在这里插入图片描述
4. 如何破坏双亲委派模型
了解了双亲委派模型,那么双亲委派模型能破坏么,就是让对象在初始化的时候就加载我们自己写的类,而不是从下往上再往下依次查找,
答案是当然可以破坏,我们都已经知道了他的原理,那么就可以破坏,而且目前像tomcat和mysql都已经破坏了双亲委派模型,那么是如何破坏的呢?
第一种方式:重写loadClass方法:
我们知道双亲委派模型的核心就是loadClass方法,就是在该方法中去依次从下到上再到下去查找,那么我们重写的方法中我们可以直接写死,就只在当前应用类加载器中去查找,找到后直接通过反射创建对象。
代码如下:

public class Test extends ClassLoader {

  public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    Class<?> c = new Test().loadClass("com.test.User", false);
    User user = (User)c.newInstance();
  }

  @Override
  protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    Class<?> c = findLoadedClass(name);
    if (c == null) {
      Class<?> aClass = Class.forName(name);
      return aClass;
    }
    return c;
  }
}

第一种方式:通过SPI破坏双亲委派模型
SPI破坏双亲委派模型的原理是因为他指定了只在当前应用类加载器中进行加载,类似于下面这行代码:
在这里插入图片描述
相当于SPI指定在当前应用类加载器中进行加载,找到了则通过反射创建对象,否则什么都不返回,当然,我们在使用SPI的时候,需要注意,需要在当前resources目录下创建META-INF/services包,然后在创建文件,文件的命名必须是接口的全路径,然后把需要初始化的对象全路径写在文件中即可,具体使用方法可以去网上查找,比如mysql的Driver该类就是通过SPI打破双亲委派机制的,因为java的类库中也存在该类,具体截图给大家展示:
在这里插入图片描述

  1. jvm内存模型
    在这里插入图片描述
    如图:JVM主要分为四大模块:栈(虚拟机栈,本地方法栈),堆,程序计数器,方法区(元空间),其中堆和方法区是线程共享的,其他模块是线程私有的,堆是我们JVM中最大的一块区域,当ClassLoader将字节码文件加载进来后,进行初始化,初始化之后的实例就放在堆内存中,对象的引用则在栈中。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值