springboot 聚合模块启动 找不到或无法加载主类_JVM之类加载机制详解

一、java程序如何运行的

当我们写好一段java代码之后,jvm是如何帮助我们执行这段程序代码的?底层的执行机制是什么?

带着以上两个问题,我们进行一下深入研究。

首先清楚一点,java是一个面向对象的语言,在java中一切皆对象,也就是java中的类,我们的任何代码都无法脱离类而独自存在,如果你想要执行java程序代码,那么必然就需要有一个主类,里面有一个程序启动的入口方法,也就是所谓的public static void main(String[] args){ }静态方法,而JVM就是通过这个入口开始运行程序代码的。

我们知道JVM底层是使用C++语言实现的,当我们启动程序时,相当于创建了一个JVM进程运行在操作系统中,JVM会把我们运行的程序代码编译成.class文件,所谓的字节码文件,保存到磁盘中,然后找到执行主类的字节码文件加载到jvm内存中(方法区),加载对应的类,调用启动方法main()。

jvm调用一些java类的方法时,是需要先进行类的加载的,只有被加载好的类才能被使用。

那么类加载,需要一个专门用来加载类的工具,我们称之为类加载器。

ad88d041cd1837e46de637521dc5a4d7.png
  1. 首先C++帮我们创建jvm
  2. C++创建一个引导类加载器,它是最顶层的类加载器
  3. 然后C++调用java的代码创建jvm的启动器Launcher的实例 Launcher由引导类加载器加载
  4. 创建Launcher实例过程中,会创建两个加载器:ExtClassLoad 和 AppClassLoad 加载器
  5. 然后使用AppClassLoad加载器按照双亲委派机制加载需要加载的类,之后调用main方法启动。
  6. java程序执行结束,jvm销毁。

二、类加载器

java类的加载是使用类加载器加载,那么类加载器有哪些?分别负责加载哪些类?如何工作的?

类加载器有哪些?分别负责加载哪些类?

  1. 引导类加载器(BootStrapClassLoader):负责加载支撑jvm运行的位于jre的lib目录下的rt.jar,charset.jar等核心类库中类
  2. 扩展类加载器(ExtClassLoader):负责加载支撑jvm运行的位于jre的lib目录下的ext扩展目下的核心类库中的类
  3. 应用类加载器(AppClassLoader):负责加载应用程序中自己创建类
  4. 自定义类加载器:负责加载自定义路径下的类

类加载过程分为:加载->验证->准备->解析->初始化

1bc4abfaa6b8eeb8972bdd9b4613b1fc.png

类加载器的创建

public 

首先C++创建引导类加载器,

然后由引导类加载器加载Launcher,在加载Launcher的初始化阶段 给launcher静态变量赋值时,调用Launcher的构造方法(由此可见,类的静态代码块先于构造方法被调用)

在构造方法中创建两个构造器:ExtClassLoader,AppClassLoader。

调用launcher.getClassLoader()方法获取类加载器,获取到加载器后 进行类的加载工作。

分析源码可见:所有的加载器都继承ClassLoader加载器

bd39326bc32eedf428486daf7ff45d05.png

类加载机制默认遵循双亲委派机制且是懒加载。

三、双亲委派机制

类加载器在加载类时默认遵循双亲委派机制

0a1987b7ab56f502e865cc7228db5a05.png

双亲委派机制

一般一个类从AppClassLoader开始,在AppClassLoader中先找是否已经加载过,如果已经加载过则直接返回,

若未加载过,则丢给父加载器ExtClassLoader加载,ExtClassLoader先找自己是否加载过,

若未加载过则丢给父加载器BootStrapClassLoader引导类加载器,BootStrapClassLoader会在自己的加载路径中找,找不到

则再回丢给ExtClassLoader加载,ExtClassLoader在自己的加载路径找 找不到回丢给AppClassLoader加载,AppClassLoader在自己加载路径中找到了 则加载成功。

双亲委派机制的优点:

  1. 沙箱安全:加入你自定义创建一个类String 全类名java.lang.String。在加载的时候,会最终丢给最顶层的引导类加载器加载,而加载器发现自己已经加载过了(加载的是jre下lib目录下的rt.jar核心库的类,并不是你创建的String类),只要全类名相同,加载器就认为加载过了,直接返回。这就防止有人对java的核心类库的类进行篡改。
  2. 避免重复加载:在整个加载过程中,一个类被任何一个加载器加载过了,就不会在此被加载,避免了重复加载。

四、实现原理

类加载是由各个加载器加载的,每个加载类都有两个核心方法 loadClass() 和findClass();

protected 

加载器会一直向上委托父加载器进行加载,最顶层的加载器加载失败,才会向下传递由子加载器加载,为双亲委派机制就体现在loadClass(String name, boolean resolve)方法中

42388ead1208539977ca6405750af243.png

五、如何打破双亲委派机制

上文已经说过双亲委派机制就是在loadClass()方法实现的,那么我们如何打破双亲委派机制呢?

打破双亲委派,我们只需要在类加载时,不在委托父加载器就可以了,就那么简单,如何实现?

自定义加载器,重写loadClass()就可以了。

/**

代码运行结果:

dc74e5476a9e478c9db2908a17c9cd0e.png

打破双亲委派机制成功!!!

我们熟知的tomcat内部就没有使用默认的双亲委派机制,内部打破了双亲委派机制。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值