面试又被问 JVM 的类加载器?直接把这篇文章丢给他!

4 篇文章 0 订阅

类加载器—Class Loader

通过上一篇博客,我们知道了
JVM 体系结构(👈可点击查看)
但对于JVM体系结构中每个结构的详细内容以及工作机制,我们还不了解。
所以,我们今天来说一下关于 类加载器 的具体内容以及工作机制。

先回顾一下 JVM 体系结构:


我们知道 类加载器是用来将 .class 文件加载进内存。

那么思考几个问题:

  1. 加载过程有哪些步骤?总不会只是简单将文件放到内存中去吧?
  2. 加载过程中做了什么操作?是直接将 .class 文件放进内存吗?如果是这样的话,那就没必要用这个加载器了,直接放进去放进去不就好了。既然不是将 .class 文件直接放进内存,那么放进内存前对 .class 文件做了什么处理呢?
  3. 类加载的时候有什么机制吗?遵循什么规则吗?

带着这些问题往下看。

类加载过程

类加载过程: 加载→验证→准备→解析→初始化
(过程中的这个加载指的是类加载过程中的一个步骤,不要弄混了。)

下面再来聊一聊过程中每个步骤的具体内容:

一、加载

  • 加载这个步骤完成了三件事
  1. 通过类的完全限定名称获取定义该类的二进制字节流。
  2. 将该字节流表示的静态存储结构转换为方法区的运行时存储结构。
  3. 在内存中生成一个代表该类的 Class 对象,作为方法区中该类各种数据的访问入口。
  • 其中获取二进制字节流的方式有:
  1. 从 ZIP 压缩包中去读取,成为 JAR、EAR、WAR 格式的基础。
  2. 从网络中获取,最典型的应用是 Web Applet。
  3. 运行时计算生成,最典型的应用是动态代理技术。
  4. 由其他文件生产,最典型的应用是 JSP 。
  5. ……

二、验证

  • 验证是连接的第一步,目的是确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并保证这些信息被当作代码运行后不会危害虚拟机自身的安全。

三、准备

  • 准备是正式为类中定义的变量(即静态变量,被 static 修饰的变量)分配内存并设置变量初始值的阶段。

四、解析

  • 解析是将常量池的符号引用替换为直接引用的过程。

五、初始化

  • 初始化是类加载过程的最后一步,在这一步之前,几乎所有动作都由Java虚拟机来主导控制,而到了这一步,Java虚拟机才开始真正执行类中编写的Java程序,并将主导控制权交给应用程序。
  • 初始化的时机:一个类只有被主动引用的时候会触发初始化。
  • 主动引用的场景(6种情况):
  1. 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。能产生这个四条指令的典型 Java 代码场景:
    ① 使用 new 关键字实例化对象的时候;
    ② 读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;
    ③ 调用一个类的静态方法的时候。
  2. 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
  3. 当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
  4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类;
  5. 当使用 JDK 7 新加入的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果是 REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。
  6. 当一个接口中定义了 JDK 8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

类加载器类型

JVM 中内置了三个重要的类加载器:

  • Bootstrap Class Loader(启动类加载器)
  • Extension Class Loader(扩展类加载器)
  • Application Class Loader(应用程序类加载器)

除了着这三个内置的类加载器,我们也可以自定义类加载器(User Class Loader)

类加载器的进一步介绍:

一、Bootstrap Class Loader(启动类加载器)
启动类加载器是最顶层的加载类,由 C++ 实现,负责将 %JAVA_HOME%/lib目录中,或者或被 -Xbootclasspath参数指定的路径中的,且能被虚拟机识别的类库 加载到虚拟机内存中。

二、Extension Class Loader(扩展类加载器)
扩展类加载器主要负责将 %JAVA_HOME%/lib/ext 目录中,或被 java.ext.dirs 系统变量所指定的路径下的类库 加载到内存中。

三、Application Class Loader(应用程序类加载器)
应用程序类加载器负责将用户类路径(ClassPath)上所指定的类库 加载到内存中

User Class Loader(自定义类加载器)
JVM内置这几个类加载器之后加载指定的类库,如果我们想加载自己的类库,我们就可以来自定义一个Class Loader,并且还可以根据需求,对这些类库进行加密解密等。


有这么多类加载器,那么当一个类需要加载进虚拟机内存时,是怎么选择类加载器的?接下来就是关于类加载器的选择。

类加载器的选择——双亲委派模型

上面那个类加载器的图中其实包含了一个模型:双亲委派模型
我们拿出来看一下:

双亲委派模型介绍: 每个类加载器会首先将类加载请求转发到父类加载器(也就是双亲委派模型中上一层的类加载器),直到最顶层的Bootstrap Class Loader,只有当父类加载器无法完成时才尝试自己加载。

简单来说就是:每个类加载器都会先看看自己的父类能不能完成这个类加载请求,父类不能完成再到自己。

双亲委派机制的好处: 沙箱安全,保证了Java 的核心 API 不被篡改,避免了类的重复加载,保证了Java程序的稳定运行。
比如,我们自己写了个 java.lang.String 类,当程序运行的时候,双亲委派机制会保证程序加载进内存的是Java自带的 java.lang.String 类,不会因为我们写的类而使得 Java 核心 API 被篡改。


关于 JVM 类加载器相关的内容就写到这里,想了解更深更细致的内容,推荐大家可以去看看 周志明大神写的 《深入理解Java虚拟机》。


注:如果猿兄这篇博客有任何错误和建议,欢迎大家留言,不胜感激!

这里先将最近要更新的JVM相关博客码在这里,写了之后再更新链接。

JVM 系列文章相关推荐:

  1. JVM 体系结构概述
  2. JVM 类加载器类型及类加载机制
  3. JVM 堆内存与垃圾回收
  4. 堆内存调优入门。(暂未更新)
  5. ……(持续更新)
  6. JVM 相关面试题及解答。(暂未更新)

持续更新,点个关注,不再迷路

这里是 猿兄,为你分享程序员的世界。

非常感谢各位优秀的程序员们能看到这里,如果觉得文章还不错的话,求点赞👍 求关注💗 求分享👬,对我来说真的 非常有用!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值