【Android 内存优化】Java 内存模型 ( Java 虚拟机内存模型 | 线程私有区 | 共享数据区 | 内存回收算法 | 引用计数 | 可达性分析 )

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: 【Android 内存优化】Java 内存模型 ( Java 虚拟机内存模型 | 线程私有区 | 共享数据区 | 内存回收算法 | 引用计数 | 可达性分析 )

文章目录

一、 Java 虚拟机内存模型

二、 程序计数器 ( 线程私有区 )

三、 虚拟机栈 ( 线程私有区 )

四、 本地方法栈 ( 线程私有区 )

五、 方法区 ( 共享数据区 )

1. 方法区

2. 运行时常量池

六、 堆区 ( 共享数据区 )

七、 内存溢出类型

八、 引用计数算法回收内存

九、 可达性分析算法回收内存





一、 Java 虚拟机内存模型


Java 内存优化 , 首当其冲就是处理 Java 内存泄漏问题 , 这是 Java 程序最主要的内存问题 , 大量的内存泄漏会导致内存溢出 ;




Java 虚拟机内存机制 : Java 虚拟机中内存分为两部分 , 线程私有部分 , 共享数据区 ;



① 共享数据区 : 方法区 ( Method Area ) , 堆区 ( Heap Area ) ; 其中方法区中包含常量池 ;


② 线程私有数据区 : 程序计数器 ( PC ) , 虚拟机栈 ( VM Stack ) , 本地方法栈 ( Native Method Stack ) ;



这是 Java 虚拟机规范定义的内存分区 , 但是具体的厂家实现可能不完全一致 , 如 Sun JDK , Open JDK 等 ;


Android 中的 Java 虚拟机 跟上述 Java 规范有很大不同 ;






二、 程序计数器 ( 线程私有区 )


程序计数器 :



① 作用 : 该内存空间很小 , 主要用于指示执行的代码行 , 程序计数器指向的代码行 , 就是下一行将要执行的代码 ;


② 线程切换运行 : Java 多线程是抢占式执行的 , 经常出现线程 A AA 执行时 , 切换到线程 B BB , 如果线程 B BB 执行完毕回到线程 A AA , 这里就需要记住线程 A AA 之前执行到哪了 , 这就需要用到线程私有的数据区的程序计数器 ( PC ) ;


③ 执行 Java 代码 : 线程执行 Java 代码时 , 程序计数器记录的是虚拟机字节码地址 ;


④ 执行 Native C/C++ 代码 : 线程执行 native 代码时 , 程序计数器记录的 值是空值 null ;



程序计数器 区域没有定义 内存溢出 异常 , 这个区域很小 ;






三、 虚拟机栈 ( 线程私有区 )


1. 虚拟机栈 ( VM Stack ) : 其生命周期与线程相同 , 描述的是 Java 方法执行的内存模型 , 该区域就是栈区 , 与堆区相对应 ;



2. 虚拟机栈中保存的数据 :


局部变量表

操作栈

方法返回地址

动态链接

额外附加信息





四、 本地方法栈 ( 线程私有区 )


本地方法栈 ( Native Method Stack ) : 这是 Native 层 C/C++ 提供的栈内存空间 , 该内存的类型与虚拟机栈内存类型一样 , 只是语言不同 , 一个 Java 方法的额栈 , 一个是 C/C++ 方法的栈 ;



Hotspot VM 虚拟机中 , 虚拟机栈 与 本地方法栈是一块内存 , 二者合二为一 ;






五、 方法区 ( 共享数据区 )




1. 方法区


方法区 : 存储以下内容 ;


类信息 , 如 ClassLoader 加载的 Class

常量 , 存放在运行时常量池中 , 该常量池也是方法区的一部分 ;

静态变量 , static 变量

即时编译器( JIT compiler ) 编译后的代码


不同的虚拟机 , 实现不同 ;


该区域一般不进行 GC 垃圾回收 ;





2. 运行时常量池


运行时常量池 :


编译中的 Java 常量 ( public static final )

字符串常量 ( String )

final 修饰的常量 ;

符号引用 , 如 类或接口完整名称 ( 带包名 ) , 字段名 , 方法名 , 描述符 ;





六、 堆区 ( 共享数据区 )


Java 堆区 :



① 最大区域 : 该内存区是 Java 虚拟机管理的内存中最大的部分 , 是垃圾回收算法 GC 的主要操作区域 ;


② 内存溢出 : OOM ( OutOfMemory ) 内存溢出就是该区域内存被全部占用 , 无法为新的内存申请更多空间 ;






七、 内存溢出类型


内存溢出 :



① 栈内存溢出 : 在 Java 的栈区内存溢出 , 就是 StackOverflowException 栈溢出异常 , 在递归的时候 , 如果没有控制好 , 就会报该异常 ;


② 堆内存溢出 : 在 Java 堆内存中的溢出 , 就是 OutOfMemoryError 堆内存溢出 , 在加载大量数据到内存时 , 会出现该异常 ;






八、 引用计数算法回收内存


引用计数是早期的 GC 回收 Java 对象机制 , 有一定弊端 ;



1. 引用计数简介 : 使用对象的引用计数 , 确定 Java 对象是否存活 , 确定是否应该被回收 ;



2. 引用计数垃圾回收算法示例说明 :



① 创建对象 : 创建一个 O OO 类型对象 o oo , 此时引用计数为 0 , 如果不将其赋值给一个变量 , 那么很快就会被回收 ;


② 变量 A AA 赋值 : 创建一个 O OO 类型对象 o oo , 将对象 o oo 其 赋值 给变量 A AA , 此时该对象 A AA 引用计数为 1 11 ;


③ 变量 B BB 赋值 : 创建一个 O OO 类型对象 o oo , 将对象 o oo 其 赋值 给变量 B BB , 此时该对象 B BB 引用计数为 1 11 ;


④ B BB 引用 A AA : 变量 B BB 中有 O OO 类型成员变量 , 将 A AA 赋值 给该成员变量 , 此时对象 B BB 引用计数变成 2 22 ;


⑤ A AA 引用 B BB : 变量 A AA 中有 O OO 类型成员变量 , 将 B BB 赋值 给该成员变量 , 此时对象 A AA 引用计数变成 2 22 ;



此时即使把 A , B A , BA,B 两个变量都设置成 null , 每个变量的引用计数都减一 , 也无法将引用计数减为 0 00 , 该对象永远无法回收 ;



引用计数弊端 : 如果两个变量之间互相引用 , 引用计数永远不能变为 0 00 ;






九、 可达性分析算法回收内存


1. 可达性分析算法 : 以 GC Root 为分析的起点 , 查找对象的引用 , 如果找到一个对象 , 无法被 GC Root 直接或间接引用到 , 那么该对象就可以被回收了 ;



2. GC Root 对象 : GC Root 是一个对象 , 可以是如下对象 ;


虚拟机栈正在运行的引用

静态属性

常量

JNI 中的对象

GC Root 就是不会被回收的那些的变量 , Android 中就是 Application , 单例类 , 运行中的 Activity 等 ;



3. 第一次扫描回调 finalize 方法 : 对象经过可达性分析后 , 发现没有引用链可以达到 GC Root , 此时就会调用该对象的 finalize() 方法进行标记 , 开发者可以实现该方法 , 进行一些逻辑处理 :


① 释放资源 : 可以执行一些资源释放方法 , 一面出现内存泄漏 ;

② 引用自救 : 将对象赋值给指定变量 , 这样可以避免被 GC 回收内存 ;


4. 可达性分析中对对象的两次扫描 : 可达性分析时 , 需要对指定对象标记两次 , 第一次被标记时会调用该对象 finalize() 方法 , 相当于判了死缓 , 此时可以通过添加引用的方式自救 , 如果没有进行任何干预 , 第 2 22 次扫描到该对象还没有到 GCRoot 的引用链 , 此时不会调用 finalize() 方法 , 直接就被回收了 ;


目录
相关文章
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
十大排序算法-快排-希尔-堆排-归并-冒泡-桶排-选择-插入-计数-基数
十大排序算法-快排-希尔-堆排-归并-冒泡-桶排-选择-插入-计数-基数
十大排序算法-快排-希尔-堆排-归并-冒泡-桶排-选择-插入-计数-基数
|
监控 算法 Java
深入理解Java虚拟机:内存管理与性能优化
在Java的世界里,虚拟机(JVM)是幕后的守护者,它默默地支撑着每一个字节码的运行。本文将揭开JVM的神秘面纱,探讨其内存管理机制及如何通过调优提升应用性能。从堆内存的分配到垃圾回收的策略,再到实践中的性能监控与调优技巧,我们将一同走进JVM的内部世界,学习如何让我们的Java程序跑得更快、更稳。
|
存储 安全 Java
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别;什么是程序计数器,堆,虚拟机栈,栈内存溢出,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
|
存储 算法 Java
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
431 0
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
|
Java 数据安全/隐私保护
一种优秀的虚拟机内存架构 - AQ
【8月更文挑战第8天】AQ虚拟机内存架构是一种创新设计,旨在提供高效、可靠及灵活的内存管理。它通过精细划分内存并采用智能分配策略,动态调整以适应应用需求。对于高内存消耗任务,AQ预留足够连续空间避免碎片化;引入内存压缩技术以增加可用空间;具备精准垃圾回收机制提高内存利用率;同时加强安全性与稳定性防止因内存错误导致的问题。总之,AQ通过先进技术提升了虚拟机性能与稳定性。
238 3
使用qemu来dump虚拟机的内存,然后用crash来分析
使用qemu来dump虚拟机的内存,然后用crash来分析
|
存储 Java API
【Azure Developer】通过Azure提供的Azue Java JDK 查询虚拟机的CPU使用率和内存使用率
【Azure Developer】通过Azure提供的Azue Java JDK 查询虚拟机的CPU使用率和内存使用率
322 0