内存管理是Java与Android开发中的核心技术领域,涉及对象拷贝、内存结构布局、系统内存调度及优化等关键内容。
一、对象拷贝机制:深拷贝与浅拷贝
1. 核心定义
- 浅拷贝:拷贝对象与被拷贝对象的引用类型字段指向同一个堆内存地址,仅普通类型(基本数据类型)字段在各自栈内存中独立存储,修改拷贝对象的引用类型字段会影响原对象。
- 深拷贝:通过序列化、类型转换等方式,使拷贝对象与原对象的引用类型字段指向不同的堆内存地址,两者完全独立,修改一方不会影响另一方。
2. 特殊情况说明
String类型与大小不超过127的Long类型虽为引用类型,但拷贝行为与基本类型一致,修改拷贝对象不会影响原对象。这是因为它们会优先对比JVM常量池中的值(涉及虚拟机底层实现,本文不展开讨论)。
参考资料
二、Android Task Stack 核心知识点
理解Android任务栈(Task Stack)需掌握两个关键技术点:
- Activity Flag:用于控制Activity的启动模式、任务栈归属等行为;
- TaskStackBuilder:用于构建复杂的任务栈结构,适配多Activity跳转场景。
三、Java内存结构
Java内存结构由5个核心区域组成,各区域功能、存储内容及共享属性如下:
| 内存区域 | 核心功能 | 存储内容 | 共享属性 |
|---|---|---|---|
| 方法区(Method Area) | 存储类结构信息 | 常量池、静态变量、构造函数、运行时常量池等 | 所有线程共享(JVM规范将其描述为堆的逻辑部分,别名non-heap“非堆”) |
| Java堆(Heap) | 存储对象实例 | 所有Java对象实例 | 所有线程共享(GC主要工作区域) |
| Java栈(Stack) | 支持方法执行 | 栈帧(含局部变量表、操作栈、方法返回值等) | 线程私有(与线程一一对应,方法调用对应栈帧入栈,执行完成对应栈帧出栈) |
| 程序计数器(PC Register) | 保存线程执行状态 | 当前线程执行的内存地址 | 线程私有(多线程切换时恢复执行状态) |
| 本地方法栈(Native Method Stack) | 支持Native方法执行 | 与Java栈功能类似,专为Native方法服务 | 线程私有 |
补充说明
下图为JDK8版本的Java内存结构可视化示意图:

JDK8内存结构
参考资料
四、虚拟内存的核心作用
虚拟内存通过页表机制实现虚拟地址与物理地址的映射,核心作用有两点:
- 进程地址空间隔离:每个进程拥有独立页表,虚拟内存空间相互独立,进程无法访问其他进程页表,解决多进程地址冲突问题;
- 内存访问安全控制:页表项包含物理地址及权限标记(如读写权限、存在标记等),操作系统通过这些标记实现内存访问权限管控,提升系统安全性。
五、系统内存紧张时的处理机制
1. 内存申请与缺页中断流程
- 应用通过
malloc函数申请内存时,实际分配的是虚拟内存,此时未分配物理内存; - 应用读写该虚拟内存时,CPU访问发现虚拟内存未映射物理内存,触发缺页中断;
- 进程从用户态切换到内核态,由内核的
Page Fault Handler(缺页中断函数)处理; - 缺页中断函数检查是否有空闲物理内存:
- 若有,直接分配物理内存并建立虚拟内存与物理内存的映射;
- 若没有,触发内存回收流程。
2. 内存回收方式
内核通过两种方式回收内存,以满足应用内存需求:
| 回收方式 | 触发时机 | 执行特性 | 影响 |
|---|---|---|---|
| 后台内存回收 | 物理内存紧张时 | 唤醒kswapd内核线程异步回收,不阻塞进程 |
对系统性能影响较小 |
| 直接内存回收 | 后台回收速度跟不上内存申请速度时 | 同步回收,阻塞进程执行 | 对系统性能有一定影响 |
3. 可回收内存类型及回收逻辑
可回收内存分为文件页和匿名页,均基于LRU算法(优先回收不常访问的内存):
-
文件页回收:
- 干净页(未修改过的文件页):直接释放内存,无性能影响;
- 脏页(修改过的文件页):先写回磁盘再释放内存,涉及磁盘I/O,影响系统性能;
- 匿名页回收:开启Swap机制后,将不常访问的匿名页换出到磁盘,下次访问时再换入内存,涉及磁盘I/O,影响系统性能。
4. 内存回收失败后的处理:OOM机制
若直接内存回收后,空闲物理内存仍不足,会触发OOM(Out Of Memory)机制:
- OOM killer根据进程的内存占用情况和
oom_score_adj值进行打分; - 得分最高的进程被优先终止,释放内存以保障系统正常运行;
- 可通过调整进程的
/proc/[pid]/oom_score_adj值,降低目标进程被OOM killer终止的概率。
六、内存回收性能影响的解决方式
针对内存回收(尤其是磁盘I/O相关操作)导致的性能下降,可通过以下配置优化:
- 调整
/proc/sys/vm/swappiness:控制文件页与匿名页的回收倾向,优先回收文件页,减少Swap换入换出开销; - 调整
/proc/sys/vm/min_free_kbytes:设定kswapd内核线程异步回收内存的触发时机,平衡内存空闲量与回收频率; - 调整
/proc/sys/vm/zone_reclaim_mode:NUMA架构下的内存回收策略,建议设为0,优先在其他Node寻找空闲内存,避免本地Node内存不足时频繁直接回收。
七、Java内存分配核心流程
调用alloc_page()或alloc_pages()等接口分配内存时,最终会调用__alloc_pages_nodemask()函数,流程如下:
- 尝试通过low阀值快速内存分配:遍历
zonelist,判断是否有Zone满足连续页框分配需求; - 若快速分配失败,进入slowpath慢速分配流程:
- 唤醒所有Node的
kswapd内核线程,尝试以min阀值快速分配; - 分配失败则触发
kswapd内存回收; - 回收后仍不满足,通过压缩规整系统可移动页;
- 若仍不满足,对
zonelist中所有空闲页框不达标的Zone执行直接内存回收;
- 唤醒所有Node的
- 最终仍无法分配满足条件的内存,触发OOM机制。
补充示意图
-
内存优化相关示意图:内存优化
-
Android内存管理框架示意图:Android内存管理框架

