摘要:本文深度拆解 Android 面试中的高频考点,涵盖 CAS 硬件原语、synchronized 锁升级、JVM/ART 垃圾回收演进、Jetpack 核心机制以及 Kotlin 语法底层实现。


一、 并发编程:从硬件到内核

1. CAS (Compare And Swap) 无锁战神

  • 底层原语:在 Java 中通过 Unsafe 类调用 C++,最终映射为 CPU 指令 cmpxchg。在多核环境下,该指令会锁定北桥信号/缓存行,确保操作原子性。

  • 三大死穴与解药

    • ABA 问题:值变了又变回原样。解法AtomicStampedReference(加版本号)。

    • 自旋性能:高并发下空转费 CPU。解法:适时升级为 synchronized 挂起线程。

    • 单变量限制:只能保证一个变量。解法AtomicReference 封装对象。

2. 性能对决:AtomicLong vs LongAdder

  • AtomicLong:单点 CAS。所有线程死磕同一个变量,并发一高,自旋次数指数级增长。

  • LongAdder分段累加(Cell 数组)。借鉴了 ConcurrentHashMap 的分段思想,将压力分散到多个格子里。读取时通过 sum() 汇总。

  • 结论:低并发选 AtomicLong(简单、实时);高并发选 LongAdder(吞吐量巨大)。

3. synchronized 的“上位”之路(锁升级)

JVM 引入锁升级是为了避免直接调用操作系统的 Mutex(重量级锁开销极大):

  1. 偏向锁:只有你一个人来,我在你额头(对象头 ThreadID)打个标,下次你直接进。

  2. 轻量级锁:又来一个竞争者。通过 CAS 抢夺,抢不到就自旋

  3. 重量级锁:人太多了或自旋太久。直接挂起线程,交给内核管理。

  • 冷知识:锁只能升级不能降级,是为了减少 JVM 检查撤销锁的性能损耗。


二、 内存管理:JVM 与 ART 的艺术

1. 垃圾回收器的演进

  • JVM 代表作

    • CMS:标记-清除算法。首个并发回收器,追求低停顿,但容易产生内存碎片。

    • G1:将内存切成 Region,Garbage First,优先回收垃圾最多的块。

  • Android ART 深度优化

    • Concurrent Copying (CC):Android 8.0+ 默认。在后台并发移动存活对象,彻底根治碎片。

    • Generational CC:Android 10+。重点攻击“短命对象”,让 GC 变得更轻量、更无感。


三、 Jetpack 核心机制剖析

1. ViewModel:数据如何“长命百岁”?

  • 生命周期:独立于 Activity。配置变更(屏幕旋转)时,Activity 被销毁重建,但 ViewModel 存储在 ViewModelStore 中,通过 NonConfigurationInstances 机制得以幸存。

  • 避坑指南:严禁持有 View 或 Context 的强引用。需要 Context 请用 AndroidViewModel 提供的 Application 级别上下文。

2. 粘性(Sticky)事件:LiveData 与 StateFlow

  • LiveData 粘性:新订阅者能收到旧值。原理:内部维护 mVersion(全局版本)和 mLastVersion(观察者版本)。只要全局版本大,就“补发”数据。

  • StateFlow 粘性:本质是 replay = 1 的流。原理collect 开启时,代码第一行就是 emit(value) 内存当前值。

  • 区别:LiveData 靠版本号补发;StateFlow 靠内存重播,且无法彻底关闭粘性。


四、 Kotlin 语法底层“脱糖”

1. 扩展函数:静态的幻觉

  • 本质:编译后是 public static 的静态方法。第一个参数就是被扩展的对象(this)。

  • 限制:无法访问 private 成员。因为它终究是类外部的“外人”,受字节码访问权限限制。

2. lateinit vs by lazy

  • lateinit:手动赋值,必须是 var。底层只是取消了非空检查,运行期若未初始化则抛出 UninitializedPropertyAccessException

  • by lazy:晚加载,必须是 val。基于委托机制,默认开启 DCL(双重检查锁),确保线程安全且只初始化一次。

3. 泛型:擦除后的遗珠

  • 原理:JVM 是伪泛型,编译后 T 变成 Object

  • 型变口诀PECS (Producer Extends, Consumer Super) $\rightarrow$ Kotlin 版:out 协变(生产者),in 逆变(消费者)

  • 实化 reified:配合 inline,将调用方的具体类型直接写入字节码,突破类型擦除的限制。


五、 Activity 交互:生命周期的回旋镖

场景:A 页面打开 B 页面

  1. A.onPause():A 停止动画,保存数据。

  2. B.onCreate() $\rightarrow$ onStart() $\rightarrow$ onResume():B 完成初始化并可见。

  3. A.onStop():B 完全遮挡 A 后,A 才进入后台状态。

面试加分点:如果 B 是一个 Dialog 样式的 Activity,A 只会执行 onPause() 而不会执行 onStop(),因为 A 依然部分可见。


结语

底层原理不是用来背的,而是用来解释“为什么这么写”的。掌握了 synchronized 的锁升级,你就能理解性能优化的边界;掌握了 LiveData 的版本号,你就能处理诡异的 UI 重复刷新问题。


作者:Li Caijun

致力于分享最硬核的 Android 底层干货!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐