06 类加载的流程
类加载子系统可以看作是 JVM 的搬运工。通过使用 Javac 编译器,我们将编写的代码转换为.class 文件,这样类就可以被真正地加载到 JVM 中,从而实现高效地运行。借助类加载流程,我们将.class 文件中的数据结构存储到了运行时的方法区中,作为后续 Java 程序运行的基础。
因为方法区里存储了类的元数据和静态变量等,所以平时在开发的时候你需要关注方法区的内存使用,避免大量加载类以及定义过多的静态变量导致内存不足。最后还要避免类初始化的循环引用,创建类时,确保类之间的引用关系没有循环,否则可能导致类初始化阻塞。
类加载全流程
07 类加载子系统 自定义加载器
08 反射机制
反射
反射(Reflection)是 JVM 提供的运行时机制 ,它允许程序在运行期借助 Reflection API 动态加载类或获取任何类的内部信息,动态创建对象并调用其属性,即使对象类型在编译期还是未知的。而我们通常写的程序,对象的类型是在编译期就确定下来的。就像上面说到的饭店的菜单,每道菜品是固定的,无法变更。
而反射赋予了我们动态创建菜品和调整菜品的能力。所以说,虽然 Java 是一种静态语言,但是反射机制的存在使 Java 也具有了动态性,具备了运行时自我认知的能力。这样我们就可以在运行时观察甚至修改 JVM 的行为。就像进入饭店的后厨,可以观察甚至调整我们的菜品。
反射的实现原理
这个 Class 对象就是反射机制的核心,它就像打开 JVM 这个厨房的钥匙,赋予了我们打造专属于自己的菜品的能力
反射 API
// 沙拉酱版本的蘸酱小黄瓜
public static DippedSauceCucumbers createCustomizedFood() throws Exception{
//第一步:获取Class对象,就像拿到了进入饭店后厨的钥匙
Class dippedSauceCucumbersClass = DippedSauceCucumbers.class;
//第二步:用反射的方式生产一个DippedSauceCucumbers对象,就像我们去后厨自己做了一份蘸酱小黄瓜
Object dippedSauceCucumbers = dippedSauceCucumbersClass.newInstance();
//第三步:因为已经身在厨房,我们可以随意调换成我们喜欢的酱料,小小的菜单已经不能约束我们丰富的想象力了 ^_^
Field dippingSauceField = dippedSauceCucumbersClass.getDeclaredField("dippingSauce");
dippingSauceField.setAccessible(true);
dippingSauceField.set(dippedSauceCucumbers,"沙拉酱");
//第四步:基于我们调换的酱料,重新生成沙拉酱版本的蘸酱小黄瓜的菜单
Method setMenuMethod = dippedSauceCucumbersClass.getDeclaredMethod("setMenu");
setMenuMethod.setAccessible(true);
setMenuMethod.invoke(dippedSauceCucumbers);
return (DippedSauceCucumbers)dippedSauceCucumbers;
}
public static void main(String[] args) throws Exception {
DippedSauceCucumbers dippedSauceCucumbers = DippedSauceCucumbers.createCustomizedFood();
dippedSauceCucumbers.printMenu();
}
反射 API 详解
反射应用场景
第二步:Spring 通过反射设置属性值。在 BeanPostProcessor 后置处理器中,Spring 会读取属性配置,使用反射调用 set 方法为 Bean 的属性设置值。
第三步:Spring 通过反射调用 Bean 的生命周期回调方法,如 init-method 和 destroy-method。
09 SPI机制
SPI(Service Provider Interface)是一种服务发现机制,它允许应用程序在运行时动态地发现和加载实现某个接口的服务提供者。这里有两个关键词需要你特别关注,分别是“运行时”和“动态”。
SPI原理
基于“约定大于配置”的设计思想,SPI 通过在类路径下预先定义好接口和服务提供者的实现类,使应用程序可以在运行时自动发现和使用这些服务,而无需手动配置
SPI设计思想
手写一个SPI程序
在平台中使用
目前同一个接口已经有了两种实现方式,接下来就是在平台上注册和使用他们的时候了,你可以看一下平台层的代码。
10 JVM内部实现机制
JVM对象基础协议
如果数据存储的时候未按照地址对齐,很可能出现一种情况,就是当 CPU 读取的数据存在于 0x0002-0x0005 的地址空间,而 CPU 要想读取该数据时,需要先要把地址空间是 0x0000-0x0003 的数据读取出来,再把地址空间是 0x0004-0x0007 的部分读取出来,把不需要的数据去掉才能读取到需要的数据,显然这种方式是非常低效的,所以为了提升 CPU 的效率,在虚拟机里一般会设置默认对齐位。
在 HotSpot 里这个值是 8。HotSpot 虚拟机要求所有的对象大小都是 8 字节的倍数,对象填充区域起到的就是补齐填充的作用,这也是一个很经典的以空间换时间思想的应用
JVM 的 OOP-Klass 模型。这里的 oop 是 ordinary object pointer 的缩写,意思就是普通对象指针。相比于 C++,JVM 通过 oop 实现指针的对象化,并且使用直接指针,JVM 定位到内存中对象的效率非常高
ColorBook 拷贝了一份 Book 函数表,使它的函数表指针指向新的函数表,因为 ColorBook 覆写了 Book 的函数 print(),所以把函数表里覆写函数的函数指针替换成了 ColorBook 覆写的函数指针,而被调用函数在函数表里偏移量是固定的,这就是多态功能的原理。