这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战
概述
优缺点
优点:
- 不需要考虑内存管理
- 可以有效防止内存泄露,有效使用内存
- 对象不再有作用域, 对象的引用有作用域
缺点:
- 过度依赖降低解决内存溢出和内存泄露问题的能力
算法
引用计数
给每个创建的对象添加引用计数器, 被引用,计数+1, 失效时计数-1, 为0表示不能被使用。
优点:
- 实现简单,执行效率高,很好的和程序交织
缺点:
- 无法检测循环引用。
可达性分析
通过一系列GCRoot对象作为起始点, 从起始点开始向下搜索对象的路径, 搜索所经过的路径称为引用链。
可以作为GC Root的对象
- 栈帧中局部变量的reference引用所引用的队列
- 方法区中static静态引用的对象
- 方法区中final常量引用的对象
- JNI引用的对象
- 虚拟机内部引用,如基础数据类型的Class,NPE, 系统类加载器等
- 所有被同步锁持有的对象
- 反映虚拟机内部的JMXBean, JVMTI注册的回调, 本地代码缓存等。
引用
强引用
垃圾回收器不会回收,内存不足, 抛出OOM
软引用
内存空间足够不会回收, 不足就会回收。软引用和引用队列(ReferenceQueue)联合使用, 如果对象被回收,JVM将这个软引用加入到引用队列中。
弱引用
只能生存到下一次垃圾收集发生。无论内存释放足够,都会被回收。弱引用可以和一个引用队列(ReferenceQueue)联合使用, 如果对象被回收,JVM将这个弱引用加入到引用队列中。
虚引用
在任何时刻都可能被垃圾回收器回收,主要用来跟踪对象被回收的活动。虚引用必须和引用队列联合使用, 如果回收时发现有虚引用, 在回收之前, 将这个虚引用加入到引用队列中
垃圾收集算法
分代收集理论
根据生命周期将内存划分,进行分区管理,建立在两个分代假说上面
- 弱分代假说: 绝大多数对象都是朝生夕灭
- 强分代假说: 熬过多次垃圾手机过程的对象就越难以消亡
根据不同区域的特点, 划分出Minor GC, Major GC, Full GC回收类型。针对不同的区域和对象存亡特征,发展出标记-复制,标记-清除, 标记-整理算法。
标记清除算法
标记-清除不足:
- 执行效率不稳定
- 内存碎片
标记复制算法
也成为复制算法或者半区复制。
复制算法不足:
- 需要提前预留一半区域用来存放存活对象, 对象区域减少一半,整体GC频繁
- 如果存活对象较多,成本上升, 效率降低
标记整理算法
移动回收后的存活对象, 不移动停顿短,移动吞吐量高。
垃圾收集器
垃圾收集器组合
JDK8中默认是 Parallel Scavenge和 Parallel Old
JDK 9默认G1
JDK14 弃用了Parallel Scavenge和 Parallel Old, 移除了CMS
组合方式有:
- SerialGC + CMS GC ( 不建议)
- SerialGC + Serial old
- ParNew + CMS
- ParNew + serial old(不建议)
- CMS + serial old
- Parallel Scavenge + Parallel Old
- Parallel Scavenge + serial Old(不建议)
- G1
性能指标
- 吞吐量: cpu在用户代码的时间和Cpu在总消耗时间
- 暂停时间
- 内存占用
- 收集频率
Serial 收集器
单线程, 使用一个CPU或1个收集线程。采用复制算法, serial old采用标记整理算法。发生STW。
-XX:+UseSerialGC
-XX:+PrintCommandLineFlags
ParNew收集器
多线程并行回收, 其余与Serial 一样。
-XX:+UseParNewGC
-XX:ParllGCThreads
Parallel Scavenge收集器
吞吐量优先收集器, 新生代收集器, 复制算法的并行多线程收集器。
- 目标是达到一个可控制的吞吐量
- 自适应调节策略, 自动指定年轻代,Eden,SurVivor区比例
-XX:+UseParallelGC
## 最大垃圾收集停顿时间 毫秒数 不推荐
-XX:MaxGCPauseMillis
## 吞吐量大小, 大于0小于100,垃圾收集时间占总时间的大小,默认99, 也就是说允许最大1%
-XX:GCTimeRatio
## 年轻代线程数, 当cpu小于等于8, 默认cpu核数,超过8 设置 3+(5*CPU)/8
XX:ParllGCThreads
## 自适应
-XX:+UseAdaptiveSizePolicy
Serial Old收集器
单线程老年代, 标记整理, cms后备
-XX:+UseSerialGC
Parallel Old收集器
并发多线程老年代, 标记整理。
-XX:+UseParallelOldGC
CMS收集器
最短停顿时间垃圾收集, 标记清除
分为四个步骤:
- 初始标记, STW
- 并发标记
- 重新标记, STW
- 并发清除
三色标记
白色: 尚未访问过
黑色: 已经访问过,本对象的引用到的其他对象也已经全部访问过
灰色: 本对象已经访问过, 但是本对象引用的其他对象尚未访问完
在并发标记时,可能有多标和漏标。
多标会产生浮动垃圾。
漏标可能影响应用程序的准确性, 可以将漏标的对象记录下来,然后作为灰色对象重新访问。
缺点
-
并发会导致应用程序变慢,吞吐量降低。CMS默认启动的回收线程数是(处理器核心+ 3)/4
-
无法处理浮动垃圾, 可能出现Concurrent Mode Failure失败而导致Full GC 的产生
JDK5, 使用老年代68%就会进行回收, JDK6使用92%会进行回收,可以通过-XX: CMSInitiationOccu-pancyFraction的值来调整百分比。
并发失败一般有两个原因: 新生代晋升失败和浮动垃圾
-
空间碎片
+XX: +UseCMS-CompactAtFullCollection开关参数,默认开启,jdk9废弃, 在CMS 收集器不得不进行FUll GC时开启内存碎片的整理过程。 +XX: CMSFullGCsBeforeCompaction, 要求CMS收集器在执行若干次不整理空间的Full GC 后,下一次会先进行整理,默认为0,每次都整理。
G1收集器
主要针对多核CPU和大容量内存,极大概率满足GC停顿时间的同时,兼具高吞吐量
- 内存划分为多个独立的region
- 保留了分代思想, region的集合
- 充分利用多CPU, 多核环境硬件优势, 尽量缩短STW
- G1标记整理算法, 局部采取复制
- 停顿可预测
- 会维护优先列表,根据允许的时间回收价值最大的区域
region
使用G1, 将堆分为2048个大小相同的独立Region块, 每个Region根据堆实际大小而定, 为2的N次幂。
增加了Humongous内存区域, 如果超过1.5个region就放里面,视为老年代。
过程
提供了young GC 和Mix GC,都是STW的
- Young GC, 选定所有年轻代Region, 通过控制年轻代Region的个数, 来控制Young GC的时间开销
- Mixed GC: 选定所有年轻代Region,外加根据global concurrent marking统计得出的收集收益高的若干老年代Region,在用户指定的开销目标范围内尽可能选择收益高的老年代Region
四个过程:
- 初始标记
- 并发标记
- 活动信息是在应用程序运行时同时计算
- 活动信息标识在疏散暂停期间最适合回收的区域
- 最终标记
- 找到的空区域被删除并回收, 计算所有区域的区域活跃度
- 使用快照算法SATB, 比CMS算法快得多
- 筛选回收
- 同时回收年轻代和老年代
- 老年代根据活跃度选择
| 参数 | 含义 |
|---|---|
| -XX: +UseG1GC | 使用G1 |
| -XX:MaxGCPauseMillis=200 | 期望最大GC停顿时间(不保证达到) |
| -XX:InitiatingHeapOccupancyPercent=45 | mixed gc中也有阀值参数,当老年代占堆的百分比达到时触发mixed GC, 默认45 |
| -XX:NewRatio=n | 新生代/老年代, 默认2 |
| -XX:SurvivorRatio=n | Eden/Survivor大小比例,默认8 |
| -XX:MaxTenuringThreshold=n | 老年代的临界值,默认15 |
| -XX:ParallelGCThreads=n | 并行阶段回收线程数 |
| -XX:ConcGCThreads=n | 并发垃圾使用线程数 |
| -XX: G1ReservePercent=n | 设置堆内存保留为假天花板的总量, 降低提升失败的可能性, 默认为10 |
| -XX:G1HeapRegionSize=n | 使用G1堆被分成区的大小, 默认根据堆的大小算出最优解, 最小1mb,最大32m |