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

简介: 【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() 方法 , 直接就被回收了 ;


目录
相关文章
|
12天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
1月前
|
编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(上)
动态内存分配与管理详解(附加笔试题分析)
49 1
|
7天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
17天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
114 9
|
21天前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
1月前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
36 2
|
1月前
|
程序员 编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(下)
动态内存分配与管理详解(附加笔试题分析)(下)
46 2
|
9天前
|
存储 SQL 数据库
虚拟化数据恢复—Vmware虚拟机误还原快照的数据恢复案例
虚拟化数据恢复环境: 一台虚拟机从物理机迁移到ESXI虚拟化平台,迁移完成后做了一个快照。虚拟机上运行了一个SQL Server数据库,记录了数年的数据。 ESXI虚拟化平台上有数十台虚拟机,EXSI虚拟化平台连接了一台EVA存储,所有的虚拟机都存放在EVA存储上。 虚拟化故障: 工组人员误操作将数年前迁移完成后做的快照还原了,也就意味着虚拟机状态还原到数年前,近几年数据都被删除了。 还原快照相当于删除数据,意味着部分存储空间会被释放。为了不让这部分释放的空间被重用,需要将连接到这台存储的所有虚拟机都关掉,需要将不能长时间宕机的虚拟机迁移到别的EXSI虚拟化平台上。
84 50
|
30天前
|
安全 虚拟化 数据中心
Xshell 连接 VMware虚拟机操作 截图和使用
Xshell 连接 VMware虚拟机操作 截图和使用
49 4
|
1月前
|
Linux 虚拟化
vmware虚拟机安装2024(超详细)
vmware虚拟机安装2024(超详细)
277 6