如图所示,JVM被分为几大功能模块:
类加载子系统
运行时数据区(我们重点关注该模块的堆、栈、方法区)
执行引擎(一般都是JIT编译器和解释器共存)
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载、类的链接、类的初始化这三个步骤类对类进行初始化。这是一个连贯的步骤,没有意外情况的话,会一次性执行完毕,所以有时也把这三个步骤统称为类的加载或者初始化。
类的加载指的是将类的.class文件中的二进制数据读取到内存中,存放在运行时数据区的方法区中,并创建一个大的Java.lang.Class对象,用来封装方法区的数据结构。
1.验证:确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载的类的正确性。
2.准备:为类变量分配内存地址并且设置该类的默认初始化值,不会为实例变量分配初始化。
3.解析:将常量池中的符号引用转换为直接引用的过程
为类变量赋予正确的初始化值
初始化阶段就是执行类构造器方法
。
若该类有父类,那么在执行子类的
JVM支持两种类型的类加载器,分别是引导类加载器(Bootstrap ClassLoader)和自定义类加载器,常见的类加载器只有三个,分别是:Bootstrap Class Loader、Extension Class Loader以及System Class Loader。
除了Bootstrap Class Loader以为,其他的类加载器都可以通过getClassLoader()方法进行获取,因为Bootstrap Class Loader使用c/c++语言编写的,所以无法获取
这个类加载器使用c/c++语言实现,嵌套在JVM内部
它用来加载Java的核心类库(JAVA_HOME/jre/lib/rt.jar、resource.jar或sum.boot.class.path路径下的内容),用于提供JVM自身需要的类
并不集成java.lang.ClassLoader,没有父加载器
负责加载扩展类加载器和应用程序类加载器,并制定为他们的父类加载器
Java语言编写,由sum.music.Launcher<span data-formula="ExtClassLoader实现
派生于ClassLoader类
父加载器为启动类加载器
Java语言编写,由sum.music.Launcher" aria-hidden="true">AppClassLoader实现
派生于ClassLoader类
父类加载器为扩展类加载器
该类加载器为程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载
工作原理:如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行加载;如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最终会到达顶层的启动类加载器;如果父类加载器可以完成类加载任务,就成功返回。否则,子加载器才会尝试自己去加载。
graph BT A(自定义类加载器) --> C(系统类加载器) B(自定义类加载器) --> C C(系统类加载器) -- 向上委托 --> D(拓展类加载器) D --向上委托-->E(引导类加载器)
优势:
避免类的重复加载
保护程序安全,防止核心API被随意串改
自定义类:java.lang.String
自定义类:java.lang.PlutoStart
虽然自定义类String类,但是在加载自定义的String类时,会率先使用引导类加载器加载,而引导类加载器会率先加载JDK自带的文件,这样可以做到对Java核心源代码的保护,这就是。
在JVM中表示两个class对象是否为同一个类必须满足两个条件:
类的完整类名必须一致,包括包名
加载这个类的ClassLoader必须相同
JVM必须知道一个类是由启动类加载器加载还是用户类加载器加载,如果由用户类加载器加载,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中
本文章使用limfx的vscode插件快速发布