当前位置: 首页 > news >正文

【底层机制】Android GC -- 为什么要有GC?GC的核心原理?理解GC的意义

我们将从“为什么需要GC”开始,逐步深入到Android虚拟机中GC的具体实现和最佳实践。


一、 为什么要垃圾回收?

在程序运行时,我们会不断地创建对象(比如在Activity、Fragment中new一个对象),这些对象都占据着内存空间。如果只创建不销毁,内存迟早会被耗尽,导致程序崩溃(OutOfMemoryError)。

在C/C++中,内存需要程序员手动管理(malloc/free, new/delete),这很容易导致两种问题:

  1. 内存泄漏:忘记释放不再使用的内存。
  2. 悬空指针:释放了内存后,又去使用它。

GC就是为了解决这个问题而诞生的。它自动追踪和回收不再被使用的对象,释放其占用的内存,从而让程序员从繁琐的内存管理工作中解放出来,专注于业务逻辑。


二、 GC的核心基本原理:可达性分析

GC如何判断一个对象是否“存活”?

核心思想:从一系列被称为 “GC Roots” 的对象出发,向下搜索,所走过的路径称为 “引用链”。如果一个对象到GC Roots没有任何引用链相连,则说明此对象是不可用的,可以被回收。

GC Roots 通常包括以下几种:

  1. 虚拟机栈中的局部变量:正在执行的各个线程的方法栈帧中的局部变量表所引用的对象。
  2. 本地方法栈中JNI引用的对象
  3. 方法区中静态属性引用的对象:类的静态变量。
  4. 方法区中常量引用的对象:比如字符串常量池里的引用。
  5. Java虚拟机内部的引用:如基本数据类型对应的Class对象,常驻的异常对象等。
  6. 被同步锁持有的对象

简单来说:如果一个对象,从任何“根”开始都找不到它,它就是垃圾。


三、 Android虚拟机中的GC

Android主要经历了两个虚拟机时代:DalvikART。它们的GC机制有显著不同。

1. Dalvik虚拟机(Android 5.0之前)的GC

Dalvik的GC相对简单粗暴,是应用卡顿的一个重要原因。

  • 标记-清除算法

    • 标记:暂停所有用户线程(这被称为 “Stop-The-World”),从GC Roots开始,遍历所有存活的对象并打上标记。
    • 清除:遍历整个堆,回收所有未被标记的对象所占用的内存。
    • 缺点:会产生内存碎片。回收后,内存空间是不连续的,当需要分配一个较大对象时,可能无法找到足够的连续内存,从而触发另一次GC。
  • GC类型

    • GC_FOR_MALLOC: 当堆上分配内存失败时触发的GC。
    • GC_CONCURRENT: 当堆内存达到一定阈值时,尝试在后台并发执行的GC,以减少应用停顿。
    • GC_EXPLICIT: 显式调用 System.gc() 触发的GC(强烈不建议,因为它会打乱虚拟机的GC计划)。

Dalvik GC的痛点Stop-The-World 时间较长,尤其是在GC_CONCURRENT的最后阶段,也会暂停所有线程进行清理工作,导致应用卡顿、掉帧

2. ART虚拟机(Android 5.0及之后)的GC

ART在安装时就将字节码预编译成本地机器码,其GC策略也更加先进和高效,主要目标是减少停顿时间

  • 主要改进
    1. 多阶段GC:将GC过程拆分成多个阶段,很多阶段可以与用户线程并发执行。
    2. 不同种类的GC:针对不同情况使用不同的回收策略。
      • 并发标记-清除:大部分标记和清除工作与用户线程并发进行,大大减少了停顿时间。
      • 压缩GC:为了解决内存碎片问题,ART会不定期地执行压缩GC。它会移动存活的对象,将它们紧凑地排列在内存的一端,从而释放出大块的连续空闲内存。这个过程的 Stop-The-World 时间较长,但ART会尽量在后台或应用在后台时进行。
    3. 分代收集:这是现代GC算法的核心思想,ART也采用了。

四、 深入理解:分代收集理论

这是理解现代GC(包括ART)的关键。根据对象的存活周期,将Java堆划分为新生代老年代

1. 新生代
  • 存放对象:绝大多数新创建的对象。

  • 特点:“朝生夕死”,大部分对象很快变得不可达。

  • 区域划分

    • Eden区:新对象基本都分配在这里。
    • Survivor区(两个)From SurvivorTo Survivor。用于存放每次Minor GC后存活下来的对象。
  • 回收过程(Minor GC)

    1. 新对象在Eden区分配。
    2. 当Eden区满时,触发一次 Minor GC
    3. 将Eden区和From Survivor中仍然存活的对象,一次性复制到To Survivor区。
    4. 同时,为这些存活的对象“年龄”+1(每熬过一次Minor GC,年龄就加1)。
    5. 清空Eden区和刚使用的From Survivor区。
    6. 最后,From SurvivorTo Survivor的角色互换。
    • 优点:速度非常快,因为只处理一小块区域。
    • 晋升:当一个对象的年龄增长到一定程度(默认15),就会被移动到老年代
2. 老年代
  • 存放对象:经过多次Minor GC仍然存活的对象(即长时间存活的对象),以及大对象(可能直接进入老年代)。
  • 特点:对象存活率高。
  • 回收过程(Major GC / Full GC)
    • 当老年代空间不足时,会触发 Major GC,通常会伴随一次 Minor GC,因此也常被称为 Full GC
    • Full GC 会对整个堆(新生代 + 老年代)进行回收,停顿时间最长,是应用卡顿的主要元凶之一。我们的优化目标就是尽量避免或减少Full GC

五、 对Android开发的指导意义与实践

理解了GC原理,我们就能写出对GC更友好的代码,提升应用性能。

1. 内存泄漏是头号大敌

内存泄漏会导致对象永远无法被GC回收,最终引发OOM。

  • 常见场景
    • 非静态内部类/匿名类持有外部类引用:如Handler、Runnable等。如果它们在Activity销毁后仍被系统(如消息队列)持有,就会导致Activity泄漏。
    • 静态变量持有Context/View引用
    • 集合类未及时清理
    • 第三方库使用后未正确释放(如监听器、广播)。
  • 排查工具Android Profiler, LeakCanary
2. 避免创建不必要的对象

对象的创建和销毁都是有成本的。在性能敏感的代码段(如onDraw、循环体),应尽量避免创建临时对象。

  • 反面教材
    // 在onDraw中每次循环都创建新对象,会瞬间产生大量垃圾,频繁触发GC
    for (int i = 0; i < 1000; i++) {String temp = "Item " + i; // 不要这样做!canvas.drawText(temp, x, y, paint);
    }
    
  • 优化方案:将对象提升为成员变量,或使用对象池。
3. 谨慎使用 System.gc()

如前所述,显式调用GC会打乱虚拟机的优化策略,可能导致不必要的、耗时的Full GC。把内存管理的决策权交给虚拟机

4. 关注 onTrimMemory()

当系统内存不足时,会回调此方法。我们可以在这里释放一些非核心资源(如缓存图片),帮助系统减轻内存压力,从而降低自身进程被杀死和触发GC的概率。

总结

特性Dalvik GCART GC
核心目标功能实现减少停顿,提升性能
主要算法标记-清除(为主)并发标记-清除 + 分代收集 + 压缩
停顿时间较长,易引起卡顿显著缩短,更流畅
处理碎片不处理,碎片化严重通过压缩整理内存

作为Android开发者,理解GC原理的最终目的是:

  1. 写出高性能、低卡顿的代码:通过避免内存泄漏和减少不必要的对象分配。
  2. 快速定位和解决内存问题:当发生OOM或内存抖动时,能迅速找到根源。
  3. 建立良好的内存观:知道代码的每一行背后可能发生什么,做到心中有数。

希望这份详细的讲解能帮助你彻底理解Android GC!

http://www.dtcms.com/a/549138.html

相关文章:

  • 自动驾驶中的传感器技术76——Navigation(13)
  • 鸿蒙Flutter三方库适配指南: 05.使用Windows搭建开发环境
  • 律所网站建设方案书怎么写网站制作排名优化
  • 谷歌网站排名搭建一个平台要多少钱
  • 使用Node.js连接 OPC UA Server
  • h5游戏免费下载:保护堆芯
  • 怎么看网站关键词排名恩施网站制作
  • Jenkins 持续集成与部署
  • 企业查询网站有哪些深圳工业产品设计公司
  • 解析平面卷积/pytorch的nn.Conv2d的计算步骤,in_channels与out_channels如何计算而来
  • 医疗器械经营许可证识别技术通过OCR与AI技术实现资质信息自动提取,显著提升行业效率与合规管理水平
  • Lombok 不生效 —— 从排查到可运行 Demo(含实战解析)
  • 【自然语言处理】预训练02:近似训练
  • 具身智能革命:富唯智能如何用“认知机器人”重塑柔性智造新纪元
  • 孤能子视角:文明演化的适配性考验——从EIS理论看中美技术路径分野
  • itunes礼品卡兑换【完整协议方案】
  • High Rank Adaption系列文章
  • wordpress 网站迁移如何做网站霸屏
  • Rust 中 Raft 协议的云原生深度集成与实践最佳实践
  • html css js网页制作成品——掌心电视剧HTML+CSS网页设计(4页)附源码
  • 基于用户的协同过滤算法实现小说推荐算法
  • 速卖通新客优惠券采购:砍单率高的核心原因
  • 【11408学习记录】考研数学概率论核心突破:一维随机变量函数分布——公式法 分布函数法精讲!​
  • Flutter 网络通信协议:从原理到实战,选对协议让 APP 飞起来
  • 【机器学习入门】9.2:感知机的工作原理 —— 从模型结构到实战分类
  • Flutter---个人信息(3)---实现修改性别
  • 做个网站需要什么制作软件的网站
  • 河北手机响应式网站建设设计企业门户网站管理制度
  • Docker简介与优豆云环境搭建
  • 后端面试实战:手写 Java 线程池核心逻辑,解释核心参数的作用