rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • Fresco

  • 对比
  • Ashmem 内存存储(匿名共享内存)
    • Ashmem 核心特性
      • 1. 内存归属与生命周期
      • 2. 跨进程共享能力
      • 3. 内存映射(mmap)机制
    • Ashmem 在 Android 中的典型应用
      • 1. Fresco 的 Bitmap 存储(核心场景)
      • 2. 系统级应用
    • Ashmem vs 普通内存(Java 堆 / 本地堆)
    • Ashmem 简单使用流程(Native 层)
    • Android 主流的跨进程通信方式
      • 核心原理先理清
      • Ashmem 对比 AIDL/Messenger 的核心优势
      • Ashmem 的劣势(AIDL/Messenger 更优的场景)
      • 适用场景总结
    • Android 中 Ashmem(匿名共享内存)的典型应用场景
      • 系统核心组件(最核心的原生应用)
      • Android 框架层的间接应用
      • 第三方框架 / 应用的典型使用
      • Ashmem 应用的核心共性
      • 面试延伸:为什么 Android 系统大量依赖 Ashmem 而非其他 IPC 方式?
    • 问题
      • 1. Fresco 为什么比 Glide 更适合超大图片加载?
      • 2. 为什么 Glide 不用 Ashmem?
      • 3. 如何结合 Ashmem 和 AIDL 使用?
      • 4. 为什么 Binder 有传输大小限制,而 Ashmem 没有?
  • 资料

Fresco

官网 | GitHub

对比

Fresco 采用 Ashmem 内存存储 Bitmap,更适合超大图片 Fresco 用 DraweeView 替代 ImageView,底层用 AndroidBitmapFactory 避免 OOM(将图片存储在堆外内存),适合大图 / 长列表,但接入成本高

Glide 更轻量,API 更简洁,适合大多数应用。 Glide 更轻量,接入简单,满足绝大多数场景。

Ashmem 内存存储(匿名共享内存)

Ashmem(Anonymous Shared Memory,匿名共享内存)是 Android 系统基于 Linux 共享内存机制封装的跨进程内存共享方案,核心特点是内存不归属于单个进程、可被内核回收、低拷贝 / 零拷贝,最典型的应用是 Fresco 框架用它存储 Bitmap 以规避 OOM。

Ashmem 核心特性

1. 内存归属与生命周期

  • 普通内存(如 Java 堆 Bitmap):归属于进程自身,进程占用的内存会计入「PSS / 私有内存」,内存满时易触发 OOM。
  • Ashmem 内存:由 Linux 内核管理,属于「共享内存」,不计入单个进程的 Java 堆 / 本地堆,进程仅持有内存的「文件描述符」,大幅降低进程自身内存占用。
  • 内核可主动回收:Ashmem 支持设置「内存区域大小」和「回收优先级」,系统内存紧张时,内核可直接回收未锁定的 Ashmem 内存,进程可通过重新映射恢复(需提前备份数据)。

2. 跨进程共享能力

Ashmem 可通过文件描述符(fd)在多个进程间传递,实现内存数据共享(如 App 进程 ↔ 图片解码进程),且数据无需拷贝(零拷贝),相比「进程间序列化传输」性能提升显著。

3. 内存映射(mmap)机制

Ashmem 基于 mmap() 系统调用实现:将内核中的共享内存区域直接映射到进程的虚拟地址空间,进程读写该内存时,本质是操作内核空间的内存,避免「用户态 ↔ 内核态」的数据拷贝。

Ashmem 在 Android 中的典型应用

1. Fresco 的 Bitmap 存储(核心场景)

Fresco 是 Facebook 推出的图片加载框架,其「Drawee」组件将 Bitmap 存储在 Ashmem 中,而非 Java 堆,核心优势:

  • 规避 OOM:Bitmap 内存不计入 App 进程的 Java 堆,即使加载大量超大图片,也不易触发 OOM;
  • 跨进程解码:Fresco 可将图片解码任务放到独立进程(ImagePipeline 进程),解码后的 Bitmap 存储在 Ashmem 中,主进程直接映射使用,避免主进程内存占用过高;
  • 内存自动回收:系统内存紧张时,内核可回收 Ashmem 中的 Bitmap 内存,Fresco 会自动重新解码恢复。

2. 系统级应用

  • SurfaceFlinger 进程(负责屏幕渲染):用 Ashmem 共享图层数据;
  • 跨进程通信(IPC):如 ContentProvider 传递大文件数据时,用 Ashmem 替代 Binder 传输(Binder 有 1MB 数据限制)。

Ashmem vs 普通内存(Java 堆 / 本地堆)

特性Ashmem 内存普通 Java 堆内存本地堆(Native)内存
内存归属内核管理(共享)进程私有进程私有
计入进程内存统计不计入(仅占文件描述符)计入(PSS / 私有内存)计入(PSS / 私有内存)
OOM 风险极低(内核可回收)高(堆内存满则 OOM)高(本地堆满也会 OOM)
跨进程共享支持(零拷贝)不支持不支持
回收机制内核主动回收(可锁定)GC 回收(被动)手动释放(易内存泄漏)
使用复杂度高(需处理映射 / 锁定 / 解锁)低(直接 new Bitmap)中(需 JNI 操作)

Ashmem 简单使用流程(Native 层)

Ashmem 主要通过 Native 层 API 操作(Java 层无直接封装),核心步骤:

  1. 创建 Ashmem 区域:ashmem_create_region("bitmap_data", size);
  2. 锁定内存:ashmem_pin_region(fd, 0, size)(防止被内核回收);
  3. 映射内存:mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  4. 写入数据(如解码后的 Bitmap 像素数据);
  5. 解锁内存:ashmem_unpin_region(fd, 0, size)(允许内核回收);
  6. 跨进程传递:通过 Binder 传递文件描述符 fd,其他进程映射使用。

Android 主流的跨进程通信方式

Ashmem(匿名共享内存)、AIDL、Messenger 是 Android 主流的跨进程通信方式,但设计目标和核心能力差异显著。Ashmem 的核心优势聚焦于大数据传输和性能效率,而 AIDL/Messenger 更适配「小数据、方法调用 / 消息传递」场景。

核心原理先理清

通信方式底层实现核心特点
AshmemLinux 共享内存 + mmap 映射内核管理共享内存区域,进程通过内存映射直接读写,无数据拷贝
AIDLBinder 驱动定义接口,跨进程调用方法,数据通过 Binder 序列化 / 反序列化传输
MessengerBinder + Message 队列基于 AIDL 封装,以「消息」为单位通信,单线程处理,简单易用

Ashmem 对比 AIDL/Messenger 的核心优势

1. 超大数据传输能力(最核心优势)
  • Binder 传输限制:AIDL/Messenger 基于 Binder 实现,Binder 内核缓冲区默认限制为 1MB(实际可用约 896KB),传输超过该大小的数据会直接抛出 TransactionTooLargeException。

  • Ashmem 无大小限制:仅受系统剩余内存限制,可轻松传输几十 MB 甚至上百 MB 的数据(如高清图片、视频帧、大文件数据)。

    典型场景:Fresco 跨进程解码 Bitmap(几十 MB)、相机进程向 App 进程传输预览帧、跨进程传输视频切片。

2. 零拷贝 / 低拷贝,性能碾压级优势
  • AIDL/Messenger 的数据拷贝:数据需经历「用户态(发送进程)→ 内核态(Binder 缓冲区)→ 用户态(接收进程)」两次拷贝,且大对象的序列化 / 反序列化会额外消耗 CPU。

  • Ashmem 的零拷贝:

    1. 发送进程创建 Ashmem 区域并写入数据;

    2. 仅将 Ashmem 的「文件描述符(fd)」通过 Binder 传递(fd 是小整数,无大小限制);

    3. 接收进程通过 fd 将 Ashmem 内存映射到自身虚拟地址空间,直接读写内存,

      数据全程不拷贝。

      性能对比:传输 10MB 数据时,Ashmem 耗时仅为 AIDL 的 1/10 左右,CPU 占用降低 80%+。

3. 内存复用与低开销
  • AIDL/Messenger:每次传输数据都需重新序列化 / 分配内存,传输完成后数据占用接收进程的堆内存,易触发 GC / 内存抖动。
  • Ashmem:
    • 内存由内核管理,不计入单个进程的私有内存,降低进程 OOM 风险;
    • 共享内存可复用(如多次传输同类型数据时,无需重复创建内存区域);
    • 内核可主动回收闲置 Ashmem 内存,避免内存浪费。
4. 实时性更高
  • AIDL/Messenger:消息 / 方法调用需排队等待 Binder 处理,且序列化 / 反序列化有延迟,实时性差。
  • Ashmem:进程直接读写共享内存,数据更新后接收方可立即感知,无中间环节延迟,适合「实时数据同步」场景(如音视频流传输)。

Ashmem 的劣势(AIDL/Messenger 更优的场景)

Ashmem 并非 “全能”,在以下场景中不如 AIDL/Messenger:

1. 简单的方法调用 / 小数据通信
  • Ashmem 仅解决 “数据存储共享”,无法直接跨进程调用方法;而 AIDL 可直接定义接口(如 void setUserName(String name)),调用方像调用本地方法一样使用,开发效率高。
  • 传输小数据(如字符串、整型、简单对象)时,Ashmem 的 “创建内存区域 + 映射” 成本高于 AIDL/Messenger 的序列化传输。
2. 安全性与数据同步
  • AIDL/Messenger:Binder 自带权限校验(如 checkCallingPermission),可限制非法进程调用;Messenger 天然单线程处理消息,无需担心多线程同步问题。
  • Ashmem:
    • 无内置权限校验,需手动通过 UID/PID 验证进程合法性;
    • 多进程同时读写 Ashmem 时,需手动加锁(如 mutex / 信号量),否则会出现数据错乱,开发复杂度高。
3. 易用性
  • AIDL/Messenger 是 Android 上层封装的 API,Java 层可直接使用,开发成本低;
  • Ashmem 主要暴露 Native 层 API(Java 层无官方封装),需通过 JNI 调用,且需处理内存映射、锁定 / 解锁、跨进程传递 fd 等细节,上手难度大。

适用场景总结

场景推荐方式原因
传输超大数据(图片、视频帧、大文件)Ashmem无大小限制,零拷贝,性能高
跨进程方法调用(如服务端提供接口给客户端)AIDL直接定义接口,调用方式直观
简单消息传递(如通知、状态同步)Messenger单线程安全,开发简单,无需处理多线程
实时数据同步(如音视频流、传感器数据)Ashmem实时性高,无数据拷贝延迟
多进程共享可复用数据(如配置缓存)Ashmem内存复用,降低整体内存占用

Android 中 Ashmem(匿名共享内存)的典型应用场景

系统核心组件(最核心的原生应用)

1. SurfaceFlinger 进程(屏幕渲染核心)
  • 作用:SurfaceFlinger 是 Android 负责合成所有屏幕图层(Surface)的核心进程,所有 App 的 UI、状态栏、壁纸等都需通过它渲染到屏幕。
  • Ashmem 应用:
    • 各个 App 进程的 Surface 数据(如 Activity 界面的像素数据)会存储在 Ashmem 中,SurfaceFlinger 直接映射这些 Ashmem 区域进行图层合成,避免跨进程拷贝大量像素数据;
    • 合成后的最终帧数据也会通过 Ashmem 传递给显示驱动(如 GPU),实现 “零拷贝” 渲染,大幅提升屏幕刷新效率。
  • 核心优势:解决 “多进程图层数据共享 + 超大像素数据传输” 的问题,避免 Binder 1MB 限制和频繁数据拷贝。
2. 多媒体框架(MediaCodec/MediaPlayer/ 相机)
  • 音视频解码 / 编码:
    • MediaCodec 处理音视频数据时,会将原始码流、解码后的 YUV/RGB 帧数据存储在 Ashmem 中,跨进程(如 App 进程 ↔ 媒体服务进程)共享,避免拷贝;
    • 例如播放 4K 视频时,单帧数据可达几十 MB,用 Ashmem 可避免频繁的内存拷贝和 OOM。
  • 相机预览 / 拍照:
    • 相机 HAL 层(硬件抽象层)采集的预览帧、拍照后的原始数据(如 YUV 格式)会存入 Ashmem,App 进程通过 Camera API 映射该内存区域获取数据,实时性和性能远高于 Binder 传输。
3. 输入法(IME)进程
  • Android 输入法通常运行在独立进程(如搜狗输入法、系统输入法),输入法的候选词列表、键盘布局渲染数据等,会通过 Ashmem 与 App 进程共享:
    • 避免频繁通过 Binder 传输大量 UI 渲染数据;
    • 输入法界面的像素缓存也存储在 Ashmem,提升键盘弹出 / 切换的流畅度。
4. 跨进程大数据传输(ContentProvider/SyncAdapter)
  • ContentProvider 是 Android 跨进程共享数据的标准组件,当传输超大数据(如相册的高清图片、大文件)时:
    • 不会直接通过 Binder 传输数据本身,而是将数据写入 Ashmem,仅传递 Ashmem 的文件描述符(fd),接收方映射后读取;
    • 典型场景:系统相册的 MediaStore 向 App 提供超大图片时,底层依赖 Ashmem 实现高效传输。

Android 框架层的间接应用

1. BitmapRegionDecoder(超大图片局部解码)
  • 虽然BitmapRegionDecoder本身不直接使用 Ashmem,但部分定制 ROM(如华为、小米)会对其优化:
    • 将超大图片的原始数据存入 Ashmem,解码局部区域时直接映射内存,避免将整张图片加载到 Java 堆,降低 OOM 风险;
    • 第三方图库应用(如快图浏览)也会基于此思路,结合 Ashmem 实现超大图片的流畅缩放 / 滑动。
2. 内存共享的 Cursor(大数据集查询)
  • 当通过Cursor查询超大数据集(如通讯录、日志文件)时,系统会将数据集缓存到 Ashmem:
    • 多个进程查询同一数据集时,无需重复读取磁盘和分配内存,直接共享 Ashmem 中的缓存;
    • 避免每个进程都持有一份数据集副本,节省系统整体内存。
3. RenderScript(高性能计算)
  • RenderScript 是 Android 用于并行计算的框架(如图片滤镜、图像处理),其计算过程中的临时数据会存储在 Ashmem:
    • 计算进程与 App 进程共享 Ashmem 中的输入 / 输出数据,避免数据拷贝;
    • 充分利用多核 CPU/GPU,同时降低内存占用。

第三方框架 / 应用的典型使用

1. Fresco(图片加载框架)
  • 最经典的第三方应用:Fresco 将解码后的 Bitmap 存储在 Ashmem 而非 Java 堆,核心优势:
    • Bitmap 内存不计入 App 进程的 Java 堆 / 本地堆,加载大量超大图片也不易 OOM;
    • 图片解码任务可放到独立进程,解码后的 Bitmap 通过 Ashmem 共享给主进程,主进程仅持有映射引用。
2. 音视频类 App(抖音 / 快手 / 腾讯视频)
  • 短视频 / 长视频 App 的音视频帧处理:
    • 采集的视频帧、解码后的画面数据存储在 Ashmem,跨进程(采集进程 ↔ 编码进程 ↔ 渲染进程)共享;
    • 直播场景中,推流前的视频帧预处理(如美颜、滤镜)也依赖 Ashmem 实现数据共享,提升实时性。
3. 大型游戏 / 3D 应用
  • 游戏的资源加载(如纹理、模型数据):
    • 将超大纹理贴图、3D 模型数据存入 Ashmem,游戏主进程与渲染进程共享,避免重复加载;
    • 游戏多进程架构(如主进程 ↔ 资源加载进程)中,Ashmem 是核心的数据共享方式。
4. 多进程应用(微信 / 支付宝)
  • 微信的 “小程序进程”“公众号进程” 与主进程之间:
    • 共享图片、缓存数据等超大资源时,使用 Ashmem 替代 Binder,降低进程间通信开销;
    • 支付宝的支付控件、安全校验进程也依赖 Ashmem 传输敏感数据(避免数据拷贝泄露风险)。

Ashmem 应用的核心共性

所有使用 Ashmem 的场景都满足以下特征:

  1. 大数据传输 / 共享:数据量超过 Binder 1MB 限制,或频繁传输会导致性能瓶颈;
  2. 低拷贝 / 零拷贝需求:避免 “用户态 ↔ 内核态” 的多次数据拷贝,提升性能;
  3. 跨进程 / 跨线程复用:多个进程 / 线程需要访问同一份数据,避免重复存储;
  4. 内存占用敏感:需降低单个进程的内存占用,规避 OOM(如超大图片、视频帧)。

面试延伸:为什么 Android 系统大量依赖 Ashmem 而非其他 IPC 方式?

  1. 性能:零拷贝特性远超 Binder/AIDL 的 “两次拷贝”,大数据场景下性能提升一个数量级;
  2. 内存效率:内核管理内存,不计入单个进程的私有内存,降低系统整体内存压力;
  3. 灵活性:可按需锁定 / 解锁内存,系统内存紧张时内核可主动回收,平衡性能与内存占用;
  4. 兼容性:基于 Linux 底层机制,跨 Android 版本稳定,无需上层适配。

问题

1. Fresco 为什么比 Glide 更适合超大图片加载?

核心原因是 Fresco 用 Ashmem 存储 Bitmap,而 Glide 将 Bitmap 存储在 Java 堆(通过 BitmapPool 复用):

  • 超大图片(如 1080P/4K 图片)的 Bitmap 占用内存可达几十 MB,存储在 Java 堆易触发 OOM;
  • Ashmem 不计入进程堆内存,即使加载多张超大图片,主进程内存占用仍可控;
  • 但 Fresco 体积更大(≈4MB),API 更复杂,Glide 更轻量(≈1.5MB),适合绝大多数普通场景。

2. 为什么 Glide 不用 Ashmem?

  • 设计目标不同:Glide 主打「轻量、易用、适配性」,Ashmem 增加了使用复杂度和框架体积;
  • 场景适配:绝大多数 App 加载的是缩略图 / 中等尺寸图片,通过 BitmapPool 复用 + 自适应采样率即可避免 OOM,无需引入 Ashmem;
  • 兼容性:Ashmem 虽为系统接口,但跨版本适配成本高于普通内存操作。

3. 如何结合 Ashmem 和 AIDL 使用?

核心思路:用 AIDL 传递 “控制指令 + Ashmem 的 fd”,Ashmem 存储实际大数据:

  1. 服务端创建 Ashmem 区域,写入大数据,获取 fd;

  2. 通过 AIDL 将 fd(整型)和数据长度、偏移量等元信息传递给客户端;

  3. 客户端通过 fd 映射 Ashmem 内存,读取数据;

  4. 数据传输完成后,通过 AIDL 通知服务端释放 Ashmem。

    优势:兼顾 AIDL 的 “方法调用便利性” 和 Ashmem 的 “大数据传输性能”。

4. 为什么 Binder 有传输大小限制,而 Ashmem 没有?

  • Binder 的核心是 “进程间方法调用”,内核缓冲区设计为小容量(1MB),目的是避免单个进程占用过多内核资源;
  • Ashmem 是 “共享内存”,数据存储在内核态的共享区域,仅传递 fd(小整数),不占用 Binder 缓冲区,因此无大小限制。

资料

  • Android开源框架源码鉴赏:Fresco
  • Fresco源码解析
最近更新:: 2025/12/20 00:50
Contributors: luokaiwen