鸿蒙中的 GC垃圾回收
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、GC 说明
1. GC 定义
-
GC(Garbage Collection):垃圾回收,指识别并释放内存中不再使用的对象,回收内存空间
-
核心目标:自动管理内存,避免内存泄漏和内存溢出
2. GC核心算法分类
-
引用计数(Reference Counting)
-
对象追踪(Tracing GC)
二、GC 算法
1. 引用计数算法
原理:
class ObjectReference {
private refCount: number = 0;
// 增加引用计数
addReference(): void {
this.refCount++;
}
// 减少引用计数
removeReference(): void {
this.refCount--;
if (this.refCount === 0) {
this.cleanup(); // 引用为0时清理对象
}
}
private cleanup(): void {
// 释放对象资源
}
}
优点:
-
设计简单,易于实现
-
及时回收,无STW(Stop The World)暂停
缺点:
-
性能开销:每次对象操作都需要更新计数
-
循环引用问题:导致内存无法回收
循环引用示例:
class TreeNode {
children: TreeNode[] = [];
parent: TreeNode | null = null;
addChild(child: TreeNode): void {
this.children.push(child);
child.parent = this; // 形成循环引用
}
}
function createCircularReference(): void {
const nodeA = new TreeNode();
const nodeB = new TreeNode();
nodeA.addChild(nodeB); // 循环引用:nodeA ↔ nodeB
// 即使函数结束,nodeA和nodeB也无法被回收
}
2. 对象追踪算法(Tracing GC)
概念:
-
根对象(Root Objects):栈内对象、全局对象等一定存活的对象
-
可达性分析:从根对象开始遍历引用链,标记所有可达对象
-
垃圾识别:不可达对象即为垃圾
三种基本算法:
(1)标记-清扫回收(Mark-Sweep)
流程:
-
标记阶段:遍历对象图,标记所有可达对象
-
清扫阶段:回收未标记的对象内存
特点:
-
优点:不移动对象,效率较高
-
缺点:产生内存碎片
(2)标记-复制回收(Mark-Copy)
流程:
-
将内存分为两个相等区域:From空间和To空间
-
将From空间的可达对象复制到To空间
-
清空整个From空间
特点:
-
优点:无内存碎片,一次遍历完成
-
缺点:空间利用率只有50%
(3)标记-整理回收(Mark-Compact)
流程:
-
标记所有可达对象
-
将可达对象移动到内存一端
-
清理剩余空间
特点:
-
优点:无内存碎片,空间利用率高
-
缺点:性能开销较大
三、ArkTS HPP GC
1. 分代模型
内存区域划分:
Heap 内存结构:
├── 年轻代(Young Space)
│ ├── From 空间
│ └── To 空间
├── 老年代(Old Space)
├── 大对象空间(HugeObjectSpace)
├── 只读空间(ReadOnlySpace)
├── 不可移动空间(NonMovableSpace)
├── 快照空间(SnapshotSpace)
└── 机器码空间(MachineCodeSpace)
对象生命周期:
-
新对象 → 分配到年轻代From空间
-
第一次GC后存活 → 移动到To空间
-
再次GC后存活 → 晋升到老年代
2. 混合算法策略
年轻代回收:部分复制
-
使用标记-复制算法
-
适合短生命周期对象
-
回收频繁但快速
老年代回收:部分整理 + 部分清扫
-
启发式CSet选择算法:
-
统计每个区域的存活对象大小
-
优先选择存活率低的区域进行整理回收
-
对剩余区域进行清扫回收
-
存活率计算:存活率 = 存活对象大小 / 区域大小
3. 流程优化
-
并发标记:使用线程池并行执行标记任务
-
并行清扫:多线程同时进行内存清扫
-
并行复制/整理:并发执行对象移动
-
并发清理:异步清理内存空间
四、Heap 结构配置参数
1. 堆大小相关参数
| 参数名 | 范围 | 作用 |
|---|---|---|
| HeapSize | 448MB | 主线程默认堆空间总大小 |
| SemiSpaceSize | 2MB-16MB | 年轻代空间大小 |
| NonmovableSpaceSize | 2MB-64MB | 不可移动空间大小 |
| SnapshotSpaceSize | 512KB-4MB | 快照空间大小 |
| MachineCodeSpaceSize | 2MB-8MB | 机器码空间大小 |
2. Worker线程堆配置
-
HeapSize: 768MB(Worker类型线程堆空间大小)
3. 并发参数
-
gcThreadNum: 7(GC线程数量,可配置)
-
MIN_TASKPOOL_THREAD_NUM: 3(线程池最小线程数)
-
MAX_TASKPOOL_THREAD_NUM: 7(线程池最大线程数)
五、GC 流程类型
1. Young GC(年轻代GC)
-
触发机制:年轻代分配达到2MB-16MB阈值
-
场景:前台应用场景
-
特点:快速回收,主要处理新分配对象
-
日志关键词:
[HPP YoungGC]
2. Old GC(老年代GC)
-
触发机制:老年代占用20MB-300MB(动态调整)
-
场景:前台应用场景
-
特点:耗时较长(5ms-10ms),全量标记
-
日志关键词:
[HPP OldGC]
3. Full GC(完整GC)
-
触发机制:应用切换到后台、内存快照、主动调用等
-
场景:后台场景,性能不敏感
-
特点:最大限度回收内存,全量压缩
-
日志关键词:
[CompressGC]
六、GC 触发策略
1. 空间阈值触发
-
函数:
AllocateYoungOrHugeObject等分配函数 -
条件:对象申请空间达到预设阈值
-
日志标识:
GCReason::ALLOCATION_LIMIT
2. Native绑定大小触发
-
函数:
GlobalNativeSizeLargerThanLimit -
条件:Native绑定内存达到限制
3. 后台切换触发
-
函数:
ChangeGCParams -
条件:应用切换到后台状态
-
日志标识:
GCReason::SWITCH_BACKGROUND
七、SharedHeap(共享堆)
1. 结构组成
-
SharedOldSpace:共享老年代空间
-
SharedHugeObjectSpace:共享大对象空间
-
SharedReadOnlySpace:共享只读空间
-
SharedNonMovableSpace:共享不可移动空间
2. 设计目的
-
线程间共享对象,提高效率
-
节省内存,提高对象存活率
-
去除SemiSpace类型
八、总结
GC 特点:
-
基于对象追踪:避免循环引用问题
-
分代收集:针对不同生命周期对象优化
-
混合算法:结合复制、整理、清扫优势
-
并发并行:减少STW时间,提升性能
-
智能触发:Smart GC保护敏感场景性能
-
共享堆:优化多线程内存使用
优化:
-
年轻代使用复制算法,快速回收短命对象
-
老年代使用启发式选择,优先处理低存活率区域
-
并发执行GC任务,最小化主线程阻塞
-
智能调整阈值,适应不同应用场景
更多推荐



所有评论(0)