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

【Android基础回顾】七:内存管理机制

Android 的内存管理机制是一个多层次的复杂系统,旨在高效利用有限的物理内存(RAM),在保证前台应用流畅运行的同时,尽可能在后台保留更多应用以提高启动速度(多任务)。

它的核心机制结合了 Linux 内核的基础功能和 Android 特有的优化策略。

1 基于 Linux 内核的内存管理 (基础层)

1.1 分页与虚拟内存

与所有现代操作系统一样,Android 使用虚拟内存系统。每个进程运行在自己的私有虚拟地址空间中。

内核和 MMU 负责将虚拟地址映射到物理 RAM 或存储上的交换空间(尽管 Android 对传统交换的使用非常谨慎)。

1.2 内存分配

应用通过 malloc、new 等标准库调用请求内存。内核负责分配物理页帧。

1.3 内存不足终止机制

当系统内存严重不足时,Linux 内核的 OOM Killer 会被触发,根据某种策略选择并终止进程以释放内存。

2 Android 特有的优化层 (用户空间)

2.1 LMK(Low Memory Killer)

这个是 Android 对标准 Linux OOM Killer 的关键增强和替代。它更主动和可预测。
下面是LMK的一些机制:

  • 内存压力等级: LMK 定义了一系列不断升高的内存阈值。
  • 进程分类: 每个进程都有一个 oom_adj_score 或 oom_score_adj 值(范围通常从 -1000 到 1000+),数值越大表示优先级越低、越容易被杀。这个值由 ActivityManagerService 根据进程状态动态调整。
  • 按优先级终止: 当可用内存低于某个阈值时,LMK 会查找当前阈值对应的 oom_adj 级别,并终止该级别或更高级别(数值更大,即优先级更低)的进程,直到可用内存回升到安全水平。

下面是一些非系统应用常见的oom_adj级别:

调整级别常量数值范围描述
FOREGROUND_APP_ADJ0前台应用(用户正在交互)。
VISIBLE_APP_ADJ100可见应用(如弹出对话框或小部件)。
PERCEPTIBLE_APP_ADJ200可感知应用(如后台播放音乐)。
BACKUP_APP_ADJ300正在执行备份操作的进程。
HEAVY_WEIGHT_APP_ADJ400重量级后台应用(较少使用)。
SERVICE_ADJ500服务进程(后台服务)。
HOME_APP_ADJ600Home 应用(Launcher)。
PREVIOUS_APP_ADJ700上一个应用(通常优先级略高于纯后台)。
CACHED_APP_MIN_ADJ800-999空进程 / 缓存应用。最先被终止,数值越大越优先被杀。
NATIVE_ADJ1000+系统原生进程(通常不会被杀)。

2.2 应用组件生命周期与内存管理

ActivityManagerService 是核心管理者,负责启动、停止、管理四大组件(Activity, Service, BroadcastReceiver, ContentProvider)的生命周期。

当系统内存不足时,AMS 会首先尝试终止空进程(Cached App。如果还不够,按 oom_adj 优先级从低到高终止包含 Service 或其他组件的后台进程。

最后才会考虑终止可见或前台进程(这是最坏情况)。

2.3 响应内存压力,onTrimMemory() 回调

这是应用响应内存压力的主要方式。系统会根据当前内存压力等级(TRIM_MEMORY_* 常量)调用此方法。

应用应根据等级释放相应资源,下面是几个等级常量:

  • TRIM_MEMORY_RUNNING_MODERATE/CRITICAL: 应用正在前台运行,但系统开始感到压力(CRITICAL 表示可能很快被杀其他进程)。
  • TRIM_MEMORY_UI_HIDDEN: 应用 UI 刚被隐藏(如按 Home 键),是释放仅 UI 使用资源的好时机。
  • TRIM_MEMORY_BACKGROUND/MODERATE/COMPLETE: 应用在后台(LRU 列表位置不同)。COMPLETE 表示进程在列表末尾,可能很快被杀,应尽可能释放资源以争取不被杀或被杀后能快速重建。

正确响应这个回调可以显著降低应用被 LMK 终止的概率。

2.4 垃圾回收(GC)

Android 使用 分代垃圾收集器 ,通常是 ART 运行时中的 Concurrent Mark-Sweep 或其变种。

主要针对 Java/Kotlin 堆内存(对象实例)。

GC 是自动触发的(根据分配速率、堆使用情况等),我们通常不应手动调用 System.gc(),因为 ART 的 GC 策略更智能,手动调用可能打乱其节奏或造成不必要的卡顿。

整体来说,内存泄漏是导致应用内存占用过高甚至 OOM 的主要原因。

2.5 Native 内存管理

native内存还是需要通过 malloc/free、new/delete 手动管理。我们需自行负责分配和释放,否则会导致 Native 内存泄漏。

有个需要注意的点是,Bitmap 像素数据在 Android 8.0 之前分配在 Native 堆(通过 libandroid_runtime.so),之后主要分配在 Java 堆。

2.6 共享内存

Android 的共享内存机制是其高效进程间通信(IPC)和内存管理的核心基础之一,其底层实现结合了 Linux 内核原生机制 和 Android 特有的优化扩展。

2.6.1 基础层:Linux 共享内存机制

Android 基于 Linux 内核,因此继承了 Linux 的共享内存基础能力。
但是,Linux 共享内存的局限性:

缺乏精细的权限控制和生命周期管理。

未针对移动设备的小内存场景优化。

无法与 Android 的 Binder 等机制深度集成。

2.6.2 Android 的核心扩展Ashmem(Anonymous Shared Memory)

Android 在 Linux 基础上引入了 Ashmem,专门为移动场景优化,它的关键特性如下:

  • 匿名共享内存,无需依赖文件系统路径或键值,通过文件描述符(fd)传递共享内存。
  • 基于 mmap() 的零拷贝,进程通过 mmap() 直接映射同一块物理内存,避免数据复制。
  • 动态内存回收(“Unpin” 机制),允许内核在内存不足时回收未被“钉住”(pinned)的内存页(类似交换分区,但更高效)。
  • 精细化访问控制,通过 Binder 传递 fd 时,可附加权限限制(如只读)。

2.6.3 Android 的共享内存高级封装

MemoryFile,他的原理是基于 Ashmem 的 Java 封装,内部通过 JNI 调用 ashmem_create_region()。

使用场景:
适合在 Java 层共享较大数据块(如摄像头帧、传感器数据)。

MemoryFile memoryFile = new MemoryFile("my_shm", size);
memoryFile.getOutputStream().write(data); // 写入数据// 通过 Binder 传递 MemoryFile 的 FileDescriptor
ParcelFileDescriptor pfd = memoryFile.getFileDescriptor();

SharedMemory(Java,API 27+),它替代 MemoryFile,支持更精细的控制(如只读共享)和 AIDL 直接传递。

SharedMemory sharedMem = SharedMemory.create("my_shm", size);
ByteBuffer buffer = sharedMem.mapReadWrite(); // 映射为 ByteBuffer// 通过 Binder 传递 SharedMemory
bundle.putParcelable("shm", sharedMem);

2.6.4 共享内存的生命周期与同步

共享内存的生命周期管理,基于引用计数,通过文件描述符(fd)的传递和关闭控制内存释放。当所有进程关闭 fd 后,内存由内核回收。
Binder 传递 fd,Android 的 Binder 机制支持传递 fd,接收方会获得一个独立的 fd 指向同一块内存。

而关于共享内存的同步机制,本身是没有同步机制的,需要我们自己处理。

Q&A

需要频繁读写的大块内存,同时需要兼顾高效,比如需要实时读取usb摄像头的数据,有什么方案?

共享内存是个不错的选择。

持续更新中。。。

http://www.xdnf.cn/news/12319.html

相关文章:

  • 44、web实验-后台管理系统基本功能
  • MySQL——视图 用户管理 语言访问
  • 【JS进阶】ES6 实现继承的方式
  • CppCon 2015 学习:C++ Coroutines
  • LeetCode 1356.根据数字二进制下1的数目排序
  • Python异步爬虫与代理完美结合
  • Prompt Tuning:生成的模型文件有什么构成
  • 购物商城网站 Java+Vue.js+SpringBoot,包括商家管理、商品分类管理、商品管理、在线客服管理、购物订单模块
  • LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
  • uefi协议设计目的
  • linux——磁盘和文件系统管理
  • python打卡训练营打卡记录day45
  • 数学运算在 OpenCV 中的核心作用与视觉效果演示
  • 本地部署大模型实战:使用AIStarter一键安装Ollama+OpenWeb教程(含最新版本更新指南)
  • 【图像处理3D】:焦距的像素单位标定
  • 使用API有效率地管理Dynadot域名,查看域名市场中所售域名的详细信息
  • 宠物车载安全座椅市场报告:解读行业趋势与投资前景
  • MyBatis-Plus深度全解:从入门到企业级实战
  • 旋转字符串的解题思路与算法分享
  • Offline Transition Modeling via Contrastive Energy Learning
  • 【iSAQB软件架构】软件架构中构建块的视图:黑箱、灰箱和白箱及其交互机制
  • vue和uniapp聊天页面右侧滚动条自动到底部
  • 计算机网络领域所有CCF-A/B/C类期刊汇总!
  • 低代码逻辑引擎配置化实战:三步穿透审批记录查询
  • 鞋内测量新方案:Moticon传感器鞋垫OpenGo在运动科学中的平衡测试应用
  • BIM Revit教程(十一)如何使用机器学习实现 MEP 布局自动化?
  • NumPy数组操作完全指南:从入门到精通
  • 【Zephyr 系列 9】Zephyr 与设备树机制详解:如何为你的板子编写 Devicetree
  • open3d:使用彩色图和深度图生成点云
  • 拆解实战案例:电商ERP管理系统从需求到原型全流程设计