本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、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)

流程

  1. 标记阶段:遍历对象图,标记所有可达对象

  2. 清扫阶段:回收未标记的对象内存

特点

  • 优点:不移动对象,效率较高

  • 缺点:产生内存碎片

(2)标记-复制回收(Mark-Copy)

流程

  1. 将内存分为两个相等区域:From空间和To空间

  2. 将From空间的可达对象复制到To空间

  3. 清空整个From空间

特点

  • 优点:无内存碎片,一次遍历完成

  • 缺点:空间利用率只有50%

(3)标记-整理回收(Mark-Compact)

流程

  1. 标记所有可达对象

  2. 将可达对象移动到内存一端

  3. 清理剩余空间

特点

  • 优点:无内存碎片,空间利用率高

  • 缺点:性能开销较大

三、ArkTS HPP GC

1. 分代模型

内存区域划分:
Heap 内存结构:
├── 年轻代(Young Space)
│   ├── From 空间
│   └── To 空间
├── 老年代(Old Space)
├── 大对象空间(HugeObjectSpace)
├── 只读空间(ReadOnlySpace)
├── 不可移动空间(NonMovableSpace)
├── 快照空间(SnapshotSpace)
└── 机器码空间(MachineCodeSpace)
对象生命周期:
  1. 新对象 → 分配到年轻代From空间

  2. 第一次GC后存活 → 移动到To空间

  3. 再次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 特点:

  1. 基于对象追踪:避免循环引用问题

  2. 分代收集:针对不同生命周期对象优化

  3. 混合算法:结合复制、整理、清扫优势

  4. 并发并行:减少STW时间,提升性能

  5. 智能触发:Smart GC保护敏感场景性能

  6. 共享堆:优化多线程内存使用

优化:

  • 年轻代使用复制算法,快速回收短命对象

  • 老年代使用启发式选择,优先处理低存活率区域

  • 并发执行GC任务,最小化主线程阻塞

  • 智能调整阈值,适应不同应用场景

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐