目录
概述
相关文章在此总结如下:
文章 | 地址 |
---|---|
jvm基本知识 | 地址 |
jvm类加载系统 | 地址 |
双亲委派模型与打破双亲委派 | 地址 |
运行时数据区 | 地址 |
运行时数据区-字符串常量池、程序计数器、直接内存 | 地址 |
jvm中对象创建流程与内存分配 | 地址 |
jvm对象内存布局 | 地址 |
垃圾回收
垃圾回收是必须的,不然 jvm 内存很快就会满。
没有被引用的对象,称之为垃圾对象。
垃圾对象查找方式
- 引用计数法 (Reference Counting)
- 根可达分析算法 (GCRooting Tracing)
引用计数法 (Reference Counting)
当对象引用消失,对象就称为垃圾
堆内存中主要存在三种引用关系:
- 单一引用
- 循环引用
- 无引用
由上图可知,引用计数法,是无法发现循环引用
这种情况的,易发生内存泄露问题。性能也是有问题的,要全部遍历一次,这是这种计数法的缺陷。
根可达分析算法 (GCRooting Tracing)
通过 GCRoots作为对象起点向下搜索,当一个对象到 GCRoots 没有任何引用链时
,此对象是垃圾
。
引用链 (ReferenceChain) : GCRoots 搜索走过的路径
垃圾对象死亡前至少经历两次标记:
- 第一次:可达性分析,没有引用链对象会被第一次标记
- 第二次:标记后的对象会经历筛选,如果筛选不通过,则会被第二次标记
对象引用类型
对象引用有哪些?
jdk 1.2之后,java对象的引用进行了扩充:强引用、软引用、弱引用、虚引用
引用类型 | 被垃圾回收时间 | 用途 | 生存时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | jvm停止时终止 |
软引用 | 内存不足时 | 对象缓存 | 内存不足时终止 |
弱引用 | 正常GC | 对象缓存 | GC后终止 |
虚引用 | 正常GC | 类似事件回调机制 | GC后终止 |
无引用 | 正常GC | 对象的一般状态 | GC后终止 |
强引用
代码中普遍的存在,只要强引用还在,就不会被GC。
Object obj = new Object();
软引用
非必须引用,内存溢出之前进行回收,如内存还不够,才会抛出异常。
package com.fun.info;
import java.lang.ref.SoftReference;
public class Reference {
public static void main(String[] args) {
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<>(obj);
obj = null;
// 有时候会返回 null
Object o = sf.get();
System.out.println("o==" + o);
}
}
应用场景:软引用可用来实现内存敏感的高速缓存。
例如:
应用需要读取大量本地文件,如果每次读取都从硬盘读取会严重影响性能,如果一次性全部加载到内存,内存可能会溢出
可以使用软引用解决这个问题,使用一个HashMap来保存文件路径和文件对象管理的软引用之间的映射关系。
内存不足时,jvm会自动回收缓存文件对象的占用空间,有效避免 OOM
问题
HashMap<String, SoftReference<InputStream>> fileCache = new HashMap<>();
弱引用
非必须引用,只要有GC,就会被回收。
Object o1 = new Object();
WeakReference<Object> wf = new WeakReference<>(o1);
o1 = null;
// 有时候会返回 null
Object o2 = wf.get();
// 返回是否被垃圾回收器标记为即将回收的垃圾
boolean enqueued = wf.isEnqueued();
System.out.println("o1 = " + o2);
System.out.println("enqueued =" + enqueued);
- 虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被称为幽灵引用
- 作用:跟踪对象被垃圾回收的状态,仅仅是提供一种确保对象被回收后,做某些事情的机制。类似监听机制。
ThreadLocal
中ThreadLocalMap
就是用了弱引用。
清除垃圾
1.标记-清除算法 (Mark-Sweep)
- 分为
标记
和清除
两个阶段:- 标记:标记出所有需要回收对象
- 清除:统一回收掉所有对象
- 缺点:
- 执行效率不稳定
- 空间碎片:会产生大量不连续内存碎片
2.复制算法 (Copying)
- 内存分为两块,清除垃圾时,将存活对象复制到另一块
- S0和S1区就是基于这个算法诞生的
- Eden:S = 8:2
- 不用担心S区不够,由Old做内存担保
- 优缺点:
- 优点:没有内存空间碎片化
- 缺点:存在空间浪费
3.标记-整理算法 (Mark-Compact)
- 标记:标记出所有需要回收对象
- 清除:统一回收掉所有对象
- 整理:将所有存活对象向一端移动
- 优缺点:
- 优点:空间没有浪费,没有内存碎片化问题
- 缺点:性能较低
分代回收 (Generational Collection)
- 新生代:选择
复制算法
,弱分代假说 - 老年代:选择
标记-清除
或标记-整理
,强分代假说
垃圾回收器
有 8 种不同的垃圾回收器,它们分别用于不同分代的垃圾回收
新生代 (复制算法) : Serial 、ParNew 、Parallel Scavenge
老年代 (标记-清除、标记-整理):SerialOld、Parallel Old、CMS
整堆:G1、ZGC
查看jdk使用哪个gc
Last login: Mon Nov 20 08:27:51 2023 from 10.35.232.22
[root@hadoop01 ~]# /data/jdk-11.0.20/bin/java -XX:+PrintCommandLineFlags -version
-XX:G1ConcRefinementThreads=43 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=2098669376 -XX:MaxHeapSize=32178700288 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
java version "11.0.20" 2023-07-18 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.20+9-LTS-256)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.20+9-LTS-256, mixed mode)
GC-串行收集器
Serial与SerialOld
配置参数:-XX:+UseSerialGC
特点:
- Serial新生代收集器,单线程执行,使用复制算法
- SerialOld老年代收集器,单线程执行,使用标记-整理算法
- 进行垃圾收集时,必须暂停用户线程
SafePoint
垃圾回收时,是需要暂停用户线程的,如果产生垃圾的速度大于回收的速度,那永远回收不了。
SafePoint线程挂起的点主要有:
- 循环的末尾
- 方法返回前
- 调用方法的call之后
- 抛出异常的位置
并行收集器
Parallel Scavenge (Stop)
配置参数: -XX:+UseParallelGC
特点:简称 PS
- 吞吐量优先收集器,垃圾收集需要暂停用户线程
- 新生代使用并行回收器,采用复制算法
- 老年代使用串行收集器,采用标记-整理算法
Parallel Old
配置参数: -XX:+UseParallelOldGC
特点:
- PS收集器的老年代版本
- 吞吐量优先收集器,垃圾收集需要暂停用户线程,对 CPU 敏感
- 新生代使用复制算法
- 老年代使用并行收集器,采用
标记-整理算法
ParNew
配置参数: -XX:+UseParNewGC
配置参数: -XX:ParallelGCThreads=n、垃圾收集线程数
特点:
- 新生代并行ParNew,老年代串行SerialOld
- Serial的多线程片
- 单核CPU不建议使用
CMS
配置参数: -XX:+UseConcMarkSweepGC
特点:
- 低延时,减少STW(Stop-The-World)对用户的影响
- 并发收集,用户线程与收集线程一起执行,对CPU资源敏感
- 不会等待堆填满再收集,到达阀值就开始收集
- 采用
标记-清除算法
,所以会产生内存碎片
实际上是精细了标记阶段的流程,降低 STW
来实现低延时
- 初始标记阶段:会STW,标记出GCRoots可以关联到的对象 ,关联对象较少,所以很快
- 并发标记阶段:不会STW,遍历GCRoots直接对象的引用链,耗时长
- 重新标记阶段:会STW,修正并发标记期间的新对象记录
- 并发清除阶段:不会STW,清除垃圾对象,释放内存空间
Garbage-First (G1)
G1是一款面向服务端应用的全功能垃圾收集器,大内存
企业配置的主要是G1。
配置参数: -XX:+UseG1GC
特点
- 吞吐量和低延时都行的整堆垃圾收集器
- G1最大堆内存 32M2048 = 64GB,最小堆内存 1M2048=2GB,低于此值不建议使用
- 全局使用
标记-整理算法
收集,局部采用复制算法
收集 - 可预测的停顿:能让使用者指定GC消耗时间(超过这个时间,则判断无回收价值,不会回收,即筛选回收),默认是200ms。
详细流程:
- 初始标记:会STW,标记出GCRoots可以关联到的对象,耗时短
- 并发标记:不会STW,遍历GCRoots直接对象的引用链,耗时长
- 最终标记:会STW,修正并发标记期间,标记产生变动的那部分
- 筛选回收:会STW,对各个Region的
回收价值
和成本排序
,根据用户期望GC停顿时间确定回收计划
G1内存划分
**取消新生代与老年代的物理划分:**采用若干个固定大小的Region
Region区类型(逻辑上):
- Eden区
- Survivor
- Old
- Humongous 当对象的容量超过了Region的50%,则被认为是巨型对象
# 使用G1垃圾收集器
-XX:+UseG1GC
# 设置期望达到的最大GC停顿时间指标(jvm会尽力实现,但不保证达到),默认值是 200 毫秒
-XX:MaxGCPauseMillis=
# 设置的 G1 区域的大小。值是 2 的幂,范围是 1MB 到 32MB 之间
# 目标是根据最小的 java 堆大小划分出约 2048 个区域
# 默认是堆内存的 1/2000
-XX:G1HeapRegionSize=n
# 设置并行垃圾回收线程数,一般将n的值设置为逻辑处理器的数量,建议最多为8.
-XX:ParallelGCThreads=n
# 设置并行标记的线程数。将n设置为ParallelGCThreads的1/4左右。
-XX:ConcGCThreads=n
# 设置触发标记周期的 java 堆占用率阀值。默认占用率是整个 java 堆的 45%
-XX:InitiatingHeapOccupancyPercent=n
ZGC
ZGC (Z Garbage Collector) 在 jdk11 中引入 的一种可扩展的低延迟垃圾收集器,在jdk15中发布稳定版本
配置参数: -XX:+UseZGC
特点:
- <1ms最大暂停时间(jdk16是10ms,jdk16+是<1ms),不会随着堆内存增加而增加
- 适合内存8MB,16TB
- 并发,基于Region、压缩、NUMA感知、使用色彩指针、使用负载屏障
- 垃圾收集算法:标记-整理算法
- 主要目标:低延时
相关参数:
# 启用ZGC
-XX:+UseZGC
# 设置最大堆内存
-Xmx
# 打印 GC 日志
-Xlog:gc
# 打印 GC 详细日志
-Xlog:gc*
结束
至此,GC基本原理
就结束了,如有疑问,欢迎评论区留言。