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

news2025/6/6 20:25:15

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.coloradmin.cn/o/2402077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构哈希表总结

349. 两个数组的交集 力扣题目链接(opens new window) 题意:给定两个数组,编写一个函数来计算它们的交集。 说明: 输出结果中的每个元素一定是唯一的。 我们可以不考虑输出结果的顺序。 public int[] intersection(int[] nums1, int[] num…

IDEA 开发PHP配置调试插件XDebug

1、安装PHP环境 为了方便,使用的PhpStudy。 安装路径:D:\resources\phpstudy_pro\Extensions\php\php7.3.4nts 2、下载Xdebug Xdebug: Downloads 选择对应的版本下载,本次使用的是7.3。 3、配置Xdebug 在php.ini中添加Xdebug配置。 D…

奇异值分解(SVD):线性代数在AI大模型中的核心工具

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开…

MySQL——视图 用户管理 语言访问

目录 视图 用户管理 数据库权限 访问 准备工作 使用函数 mysql界面级工具 连接池 视图 这里的视图与事务中的读视图是两个不同的概念:视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。视图的…

二、Sqoop 详细安装部署教程

作者:IvanCodes 日期:2025年6月2日 专栏:Sqoop教程 Apache Sqoop 是一个强大的工具,用于在 Hadoop (HDFS, Hive, HBase) 与关系型数据库 (如 MySQL, PostgreSQL, Oracle) 之间高效传输数据。本教程将详细指导您如何根据官方网站截…

【C语言预处理详解(下)】--#和##运算符,命名约定,命令行定义 ,#undef,条件编译,头文件的包含,嵌套文件包含,其他预处理指令

目录 五.#和##运算符 5.1--#运算符 5.2--##运算符 六.命名约定,#undef,命令行定义 6.1--命名约定 6.2--#undef 6.3--命名行定义 七.条件编译 常见的条件编译指令: 1.普通的条件编译: 2.多个分支的条件编译(可以利用条…

03.搭建K8S集群

K8S集群搭建的方式 目前主流的搭建k8s集群的方式有kubeadm、minikube、二进制包三种方式: kubeadm(本案例搭建方式) 是一个工具,用于快速搭建kubernetes集群,目前应该是比较方便和推荐的,简单易用 kubea…

RDMA简介3之四种子协议对比

RDMA协议共有四种子协议,分别为InfiniBand、iWARP、RoCE v1和RoCE v2协议。这四种协议使用统一的RDMA API,但在具体的网络层级实现上有所不同,如图1所示,接下来将分别介绍这四种子协议。 图1 RDMA四种子协议网络层级关系图 Infin…

【最新版】西陆洗车系统源码全开源+uniapp前端+搭建教程

一.系统介绍 一款基于ThinkPHPUniapp开发的多门店洗车系统,包含用户端(小程序)、门店员工端(小程序)、门店端(PC)、平台管理端(PC)。 门店分连锁门店和独立门店&#xf…

Linux开发工具(apt,vim,gcc)

目录 yum/apt包管理器 Linux编辑器 vim 1.见一见vim 2.vim的多模式 3.命令模式底行模式等 4.vim的配置 Linux编译器 gcc/g 1.预处理(宏替换) 2.编译(生成汇编) 3.汇编(生成机器可识别代码) 4.连…

鸿蒙Next开发真机调试签名申请流程

背景: 在学习鸿蒙next开发应用的初期总是会遇到一堆的问题,毕竟鸿蒙next开发不管是他的ArKTS语言还是他的开发工具DevEco Studio都还在起步阶段,就像当初的Android起步一样,总会有资料不足的一些问题。就比如我们学习下载完DevEco…

[yolov11改进系列]基于yolov11引入上下文锚点注意力CAA的python源码+训练源码

【CAA介绍】 本文记录的是基于CAA注意力模块的RT-DETR目标检测改进方法研究。在远程遥感图像或其他大尺度变化的图像中目标检测任务中,为准确提取其长距离上下文信息,需要解决大目标尺度变化和多样上下文信息时的不足的问题。CAA能够有效捕捉长距离依赖…

【linux】全志Tina预编译一个so库文件到根文件系统/usr/lib/下

一、sdk中新建文件夹 路径: V:\t113\work3\t113\openwrt\package\feeds\libs\md5util md5util为需要注入的库文件夹。 文件结构 libs md5util files libmd5util.so makefile etc.. 二、编写makefile include $(TOPDIR)/rules.mkPKG_NAME : md5util PKG_VERSIO…

C# 类和继承(成员访回修饰符)

成员访回修饰符 本章之前的两节阐述了类的可访问性。对类的可访问性,只有两种修饰符:internal和public。 本节阐述成员的可访问性。类的可访问性描述了类的可见性;成员的可访问性描述了类成员的可 见性。 声明在类中的每个成员对系统的不同…

Linux-文件管理及归档压缩

1.根下的目录作用说明: /:Linux系统中所有的文件都在根下/bin:(二进制命令目录)存放常用的用户命令/boot:系统启动时的引导文件(内核的引导配置文件,grub配置文件,内核配置文件) 例…

微软认证考试科目众多?该如何选择?

在云计算、人工智能、数据分析等技术快速发展的今天,微软认证(Microsoft Certification)已成为IT从业者、开发者、数据分析师提升竞争力的重要凭证。但面对众多考试科目,很多人不知道如何选择。本文将详细介绍微软认证的考试方向、…

Dify工作流实践—根据word需求文档编写测试用例到Excel中

前言 这篇文章依赖到的操作可查阅我之前的文章: dify里的大模型是怎么添加进来的:在Windows本地部署Dify详细操作 flask 框架app.route()函数的开发和调用:PythonWeb开发框架—Flask工程创建和app.route使用详解 结构化提示词的编写&…

【LC实战派】小智固件编译

这篇写给立创吴总,是节前答应他配合git代码的说明;也给所有对小智感兴趣的小伙伴。 请多提意见,让这份文档更有价值 - 第一当然是拉取源码 - git clone https://github.com/78/xiaozhi-esp32.git 完成后,先查看固件中实际的…

jdbcTemplate.query备忘录

jdbcTemplate.query中使用全部字符串和参数注入&#xff0c; 查询速度为什么差距这么大 如何正确使用JdbcTemplate参数化查询 1、使用?占位符 String sql "SELECT * FROM users WHERE name LIKE ?"; List<User> users jdbcTemplate.query(sql,new Object[…

如何搭建Z-Blog PHP版本:详细指南

Z-Blog是一款功能强大且易于使用的博客平台&#xff0c;支持PHP和ASP两种环境。本文将重点介绍如何在PHP环境下搭建Z-Blog博客系统&#xff0c;帮助您快速上线自己的个人博客站点。 准备工作 1. 获取Z-Blog PHP版本 首先&#xff0c;访问Z-Blog官方网站下载最新版本的Z-Blog…