Java/Android内存核心知识点全解析:拷贝机制、内存结构与内存管理

内存管理是Java与Android开发中的核心技术领域,涉及对象拷贝、内存结构布局、系统内存调度及优化等关键内容。

一、对象拷贝机制:深拷贝与浅拷贝

1. 核心定义

  • 浅拷贝:拷贝对象与被拷贝对象的引用类型字段指向同一个堆内存地址,仅普通类型(基本数据类型)字段在各自栈内存中独立存储,修改拷贝对象的引用类型字段会影响原对象。
  • 深拷贝:通过序列化、类型转换等方式,使拷贝对象与原对象的引用类型字段指向不同的堆内存地址,两者完全独立,修改一方不会影响另一方。

2. 特殊情况说明

String类型与大小不超过127的Long类型虽为引用类型,但拷贝行为与基本类型一致,修改拷贝对象不会影响原对象。这是因为它们会优先对比JVM常量池中的值(涉及虚拟机底层实现,本文不展开讨论)。

参考资料

二、Android Task Stack 核心知识点

理解Android任务栈(Task Stack)需掌握两个关键技术点:

  1. Activity Flag:用于控制Activity的启动模式、任务栈归属等行为;
  2. 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. 进程地址空间隔离:每个进程拥有独立页表,虚拟内存空间相互独立,进程无法访问其他进程页表,解决多进程地址冲突问题;
  2. 内存访问安全控制:页表项包含物理地址及权限标记(如读写权限、存在标记等),操作系统通过这些标记实现内存访问权限管控,提升系统安全性。

五、系统内存紧张时的处理机制

1. 内存申请与缺页中断流程

  1. 应用通过malloc函数申请内存时,实际分配的是虚拟内存,此时未分配物理内存;
  2. 应用读写该虚拟内存时,CPU访问发现虚拟内存未映射物理内存,触发缺页中断
  3. 进程从用户态切换到内核态,由内核的Page Fault Handler(缺页中断函数)处理;
  4. 缺页中断函数检查是否有空闲物理内存:
    • 若有,直接分配物理内存并建立虚拟内存与物理内存的映射;
    • 若没有,触发内存回收流程。

2. 内存回收方式

内核通过两种方式回收内存,以满足应用内存需求:

回收方式 触发时机 执行特性 影响
后台内存回收 物理内存紧张时 唤醒kswapd内核线程异步回收,不阻塞进程 对系统性能影响较小
直接内存回收 后台回收速度跟不上内存申请速度时 同步回收,阻塞进程执行 对系统性能有一定影响

3. 可回收内存类型及回收逻辑

可回收内存分为文件页和匿名页,均基于LRU算法(优先回收不常访问的内存):

  • 文件页回收
    • 干净页(未修改过的文件页):直接释放内存,无性能影响;
    • 脏页(修改过的文件页):先写回磁盘再释放内存,涉及磁盘I/O,影响系统性能;
  • 匿名页回收:开启Swap机制后,将不常访问的匿名页换出到磁盘,下次访问时再换入内存,涉及磁盘I/O,影响系统性能。

4. 内存回收失败后的处理:OOM机制

若直接内存回收后,空闲物理内存仍不足,会触发OOM(Out Of Memory)机制

  1. OOM killer根据进程的内存占用情况和oom_score_adj值进行打分;
  2. 得分最高的进程被优先终止,释放内存以保障系统正常运行;
  3. 可通过调整进程的/proc/[pid]/oom_score_adj值,降低目标进程被OOM killer终止的概率。

六、内存回收性能影响的解决方式

针对内存回收(尤其是磁盘I/O相关操作)导致的性能下降,可通过以下配置优化:

  1. 调整/proc/sys/vm/swappiness:控制文件页与匿名页的回收倾向,优先回收文件页,减少Swap换入换出开销;
  2. 调整/proc/sys/vm/min_free_kbytes:设定kswapd内核线程异步回收内存的触发时机,平衡内存空闲量与回收频率;
  3. 调整/proc/sys/vm/zone_reclaim_mode:NUMA架构下的内存回收策略,建议设为0,优先在其他Node寻找空闲内存,避免本地Node内存不足时频繁直接回收。

七、Java内存分配核心流程

调用alloc_page()alloc_pages()等接口分配内存时,最终会调用__alloc_pages_nodemask()函数,流程如下:

  1. 尝试通过low阀值快速内存分配:遍历zonelist,判断是否有Zone满足连续页框分配需求;
  2. 若快速分配失败,进入slowpath慢速分配流程
    • 唤醒所有Node的kswapd内核线程,尝试以min阀值快速分配;
    • 分配失败则触发kswapd内存回收;
    • 回收后仍不满足,通过压缩规整系统可移动页;
    • 若仍不满足,对zonelist中所有空闲页框不达标的Zone执行直接内存回收;
  3. 最终仍无法分配满足条件的内存,触发OOM机制。

补充示意图

  • 内存优化相关示意图:
    内存优化
  • Android内存管理框架示意图:
    Android内存管理框架

参考资料

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

相关阅读更多精彩内容

友情链接更多精彩内容