垃圾收集器
垃圾收集算法是理论基础,定义了如何识别和回收垃圾;垃圾收集器是算法的具体实现,一个收集器可以实现一种或多种算法
JVM 的垃圾收集器主要分为两大类:分代收集器和分区收集器
分代收集器的代表是 CMS
分区收集器的代表是 G1 和 ZGC
分代是按对象年龄划分(新生代/老年代),针对性回收;分区是把堆切成独立小区域(Region),可预测停顿时间。
现代收集器(如 G1)结合两者:逻辑上分代,物理上分区。
好好学习,天天向上
垃圾收集算法是理论基础,定义了如何识别和回收垃圾;垃圾收集器是算法的具体实现,一个收集器可以实现一种或多种算法
JVM 的垃圾收集器主要分为两大类:分代收集器和分区收集器
分代收集器的代表是 CMS
分区收集器的代表是 G1 和 ZGC
分代是按对象年龄划分(新生代/老年代),针对性回收;分区是把堆切成独立小区域(Region),可预测停顿时间。
现代收集器(如 G1)结合两者:逻辑上分代,物理上分区。
ZGC 是 JDK 11 时引入的一款低延迟的垃圾收集器,最大特点是将垃圾收集的停顿时间控制在 10ms 以内,即使在 TB 级别的堆内存下也能保持较低的停顿时间。
ZGC 也采⽤了复制算法,只不过做了重⼤优化,ZGC 在标记、转移和重定位阶段⼏乎都是并发的,这是 ZGC 实现停顿时间⼩于 10ms 的关键所在
关键技术
JVM 的操作对象是 Class 文件。JVM 把 Class 文件中描述类的数据结构加载到内存中,并对数据进行校验、解析和初始化,最终转化成可以被 JVM 直接使用的类型,这个过程被称为类加载机制。
类加载是运行时进行的,JVM 不会在启动时加载所有类,而是按需加载。
触发类加载的时机(主动引用):
new一个对象- 访问类的静态变量或静态方法
- 使用反射(如
Class.forName())- 初始化子类时(会先触发父类加载)
不触发类加载的情况(被动引用):
- 通过子类引用父类的静态变量
- 定义类数组(如
MyClass[] arr)- 常量引用(如
MyClass.CONST)
屏障 = 在某个操作前后插入的"检查点"或"钩子"
想象一个小区门禁:
居民进出 → [门禁屏障] → 记录谁进出了 → 放行
屏障不做"拦截",只做"记录"或"检查"
CPU 为了性能会重排序指令,内存屏障告诉 CPU:
指令1
指令2
[内存屏障] ← barrier 之前的指令必须先执行完
指令3
指令4
对象的内存布局是由 Java 虚拟机规范定义的,但具体的实现细节各有不同,如 HotSpot 和 OpenJ9 就不一样
常用的 HotSpot 为例:
对象在内存中包括三部分:对象头、实例数据和对齐填充
对象头 (Mark World + 类型指针 (指向 Class 对象)) + 实例数据 + 对齐填充
对象头 (8字节 + 8字节 (压缩 4 字节)): 12字节 (指针压缩)
对齐填充必须是 8 的倍数 (64位)
Java 堆被划分为新生代和老年代两个区域
新生代又被划分为 Eden 空间和两个 Survivor 空间(From 和 To)
新创建的对象会被分配到 Eden 空间
当 Eden 区填满时,会触发一次 Minor GC,清除不再使用的对象
存活下来的对象会从 Eden 区移动到 Survivor 区。
对象在新生代中经历多次 GC 后,如果仍然存活,会被移动到老年代
当老年代内存不足时,会触发 Major GC,对整个堆进行垃圾回收
内存溢出,俗称 OOM,是指当程序请求分配内存时,由于没有足够的内存空间,从而抛出 OutOfMemoryError
List<String> list = new ArrayList<>();
while (true) {
list.add("OutOfMemory".repeat(1000)); // 无限增加内存
}
垃圾回收就是对堆内存中不再被引用的(不可达的)对象进行清除或回收
JVM 在做 GC 之前,会先搞清楚什么是垃圾,什么不是垃圾,通常会通过可达性分析算法来判断对象是否存活
在确定了哪些垃圾可以被回收后
垃圾收集器(如 CMS、G1、ZGC)要做的事情就是进行垃圾回收
可以采用:
C++ 等语言创建对象要不断的去开辟空间,不用的时候又需要不断的去释放空间,既要写构造函数,又要写析构函数
于是,有人就提出,能不能写一段程序实现这块功能,每次创建对象、释放内存空间的时候复用这段代码
1960 年,John McCarthy 在设计 Lisp 语言时首次提出了垃圾回收的概念,用于自动管理内存,避免程序员手动释放内存带来的错误
"Stop The World"是 Java 垃圾收集中的一个重要概念
在垃圾收集过程中,JVM 会暂停所有的用户线程,这种暂停被称为"Stop The World"事件
这么做的主要原因是为了防止在垃圾收集过程中,用户线程修改了堆中的对象,导致垃圾收集器无法准确地收集垃圾
值得注意的是,"Stop The World"事件会对 Java 应用的性能产生影响
如果停顿时间过长,就会导致应用的响应时间变长,对于对实时性要求较高的应用,如交易系统、游戏服务器等,这种情况是不能接受的。
因此,在选择和调优垃圾收集器时,需要考虑其停顿时间。Java 中的一些垃圾收集器,如 G1 和 ZGC,都会尽可能地减少了"Stop The World"的时间,通过并发的垃圾收集,提高应用的响应性能。