一.类加载机制
1.1 加载
JVM通过一个类的全限定名来获取其定义的二进制字节流,生成一个.class对象。加载这一步主要是通过类加载器完成的。而选择类加载器则需要通过双亲委派模型决定。
双亲委派模型
模型结构
双亲委派模型的基本结构由多个类加载器组成,每个类加载器都有一个父加载器。
类加载器主要包括:
启动类加载器
应用程序类加载器
扩展类加载器
自定义类加载器
工作原理
- 请求:当一个类收到类加载请求时,它自己不会去加载,先委托给父类加载器
- 递归:父类加载器再将这个请求委派给它的父类加载器,递归向上,直到最顶层的类加载器
- 执行:最顶层的类加载器可以执行,则加载,否则返回给子类加载器执行
优点
避免类冲突:每个类只会在顶层加载一次,有效的避免了同名类的冲突
提高安全性:用户自己编写的类不会因为加载而影响到Java核心类库,降低了干扰系统核心类的风险
1.2 验证
在加载之后,JVM会对加载的字节码进行验证,确保符合Java语言的规范
文件格式验证:验证字节流是否符合.class文件的格式
元数据验证:对字节码描述的信息进行语义分析,以保证符合Java语言规范的要求
字节码验证:确保字节码不会危害JVM的安全性和稳定性
1.3 准备
JVM为类的静态变量分配内存并设置默认值
静态变量的分配:为类中的静态变量开辟内存空间,所有静态变量默认值为其类型对应的默认值
类常量的分配:对于常量,值会在加载阶段被直接赋值
1.4 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
字段解析:将被引用字段的符号引用转换为该字段在内存中的地址
方法解析:将方法调用的符号引用解析为方法在内存中的地址
1.5 初始化
初始化阶段开始执行类中定义的Java程序代码。
静态初始化块:执行类中所有的静态初始化代码和静态变量的显示赋值
父类初始化:在子类初始化之前,JVM会先初始化其父类
二. 使用
初始化完成之后,类就可以被使用,可以通过实例化类或者直接调用静态方法和字段来使用该类
三. 卸载
卸载过程通常是由Java的垃圾回收机制管理
3.1 垃圾回收机制
Java的垃圾回收机制是自动管理内存的一种方式,负责回收不再使用的对象所占用的内存空间,以防止内存泄露和优化内存使用。
内存泄露和内存溢出
泄露:内存泄漏是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费。
溢出:方法区(可以通过加载大量的类(尤其是动态生成的类)或者使用反射机制创建大量的元数据来撑爆。
3.2 内存管理
内存区域划分:
栈:存储局部变量和方法调用,自动释放内存
堆:所有对象实例的内存分配和管理区域
方法区:存储类的元数据
本地方法栈
垃圾回收
自动回收堆中不再被引用的对象的内存空间,为新的对象分配空间
3.3 判断对象是否可以回收
引用计数法:
这种方法是给每个对象添加一个引用计数器。当有一个地方引用这个对象时,计数器就加 1;当引用失效时,计数器就减 1。当计数器的值为 0 时,就表示这个对象可以被回收。
可达性分析算法:
这是 JVM 采用的主流方法。从一系列被称为 “GC Roots” 的对象开始,如栈中的局部变量、静态变量等,通过对象之间的引用关系向下搜索。如果一个对象不能通过从 GC Roots 开始的引用链到达,那么这个对象就是不可达的,就可以被回收。
3.4 垃圾回收算法
标记-清除:先标记所有活动对象,然后清除未标记的对象。缺点是会区域分配不均 产生内存碎片
标记-整理:与标记-清除类似,但在清除阶段,整理存活对象,消除内存碎片
复制算法:将内存划分为两个区域,每次只使用一个区域。当一个区域被填满后,将存活的对象复制到另一个区域,清理掉已被回收的对象。
分代算法:
年轻代:存放新创建的对象,垃圾回收频繁
老年代:存放存活时间较长的对象,垃圾回收相对较少
永久代:存放类的元数据
3.5 垃圾回收器
Serial GC:单线程的垃圾回收器
Parallel GC:多线程的垃圾回收器
Parallel Scavenge回收器:吞吐量优先
CMS回收器:低延迟
G1收集器:区域化分代式
四. JDK、JRE、JVM三者的关系
JVM是 Java 程序运行的虚拟机,负责执行 Java 字节码。
JRE是 Java 程序的运行环境,包括 JVM 和 Java 核心类库,但不包含开发工具(如编译器)。
JDK是 Java 的开发工具包,包含 JRE 和开发工具(如编译器 javac、调试器等),用于开发 Java 应用程序。