Android 虚拟机存储结构和垃圾回收机制介绍

本文详细介绍了Android虚拟机的存储结构,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆和方法区,并探讨了直接内存。同时,解释了对象的引用判断方法、垃圾回收机制,如引用计数法和可达性分析算法,以及GC的触发时机和分代收集策略。此外,还提到了减少GC开销的一些措施。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

虚拟机的存储结构分为5个部分,分别是程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区,下面就 介绍各部分的内容。

程序计数器:较小的内存空间,指定当前线程执行字节码的行数

Java虚拟机栈:线程私有的,生命周期与线程同步,每个方法执行的时候,都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出入口等信息,每个方法的调用都是栈帧入栈到出栈的过程。局部变量表用于存储方法相关的局部变量,包括基本数据、对象引用和返回地址等。栈深度大于虚拟机允许的深度,会抛出StackOverFlowError。如果当Java虚拟机允许动态扩展虚拟机栈的时候,没有内存可分配报OutOfMemoryError。

本地方法栈:和虚拟机栈一样的,只是用来执行native方法

Java堆:Java虚拟机管理内存中最大的一块,Java堆是被所有线程共享的内存区域,主要存储对象的实例

方法区:线程共享的内存区域,存储被虚拟机加载的类信息、常量、静态变量、即时编译的代码数据等。方法区在物理上是不需要连续的,可以选择固定大小或者扩展大小,还可以选择不实现垃圾收集,方法区的垃圾回收比较少,主要的回收是针对常量池和类型的卸载。运行时常量是方法区的一部分,常量池用于存放编译生成的各种字面量和符合引用。

直接内存:不是虚拟机的一部分,可以直接访问堆外的内存。

Student stu = new Student():
Student stu作为引用对象,存在Java虚拟机栈上;new Student()保存在Java堆中,堆中记录Student类型的信息包括方法、接口、对象类型等地址,这些类型的执行数据存储在方法区中。

判断对象是否存在引用:引用计数法和可达性分析算法
引用计数法:对象添加一个引用计数器,每当有一个地方引用计数器就增加1,引用失效就减1,计数器为0就不可用;缺点是无法处理对象直接相互引用的问题。
可达性分析算法:也就是常说的GC Root,每次回收时候,会从一系列的“GC root”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到“GC roots”没有任何引用链相连时,则证明此对象是不可用的。也就是当两个对象相互引用的时候,如果没有GC Roots节点可以达到两个对象,表示这两个对象都是不可达的,所以也是要被回收的。

GC触发的时机
当内存不够的时候会触发GC,或者代码手动触发GC

GC回收采用的是分代收集的算法。主要分为年轻代、年老代、持久代。
年轻代:分成Eden和2个Survivor ,大小比例是8:1:1。当一个对象被创建的时候,内存分配首先分配在年轻代,大部分对象在创建以后都不再使用,对象很快变得不可达,就会被回收掉。,由于垃圾是被年轻代清理掉的,所以被叫做Minor GC或YoungGC。采用复制算法进行回收。
年老代:对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次YoungGc后存活了下来,默认次数是15),则被保存到年老代,年老代空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数比年轻代少。当年老代内存不足时,将执行Major GC,也叫Full GC。采用标记-清理(标记-整理)算法进行回收
持久代:用于存放静态文件,比如Java类信息、方法等。持久代对垃圾回收没有显著影响,只有类卸载、常量池回收才会进行回收。

垃圾回收用到的算法:标记-复制算法、标记-清除算法、标记整理算法
标记-复制算法:分成Eden和2个Survivor 8:1:1,多次未被回收的会被放入年老代内存。主要解决效率问题,将空间分成两部分,当这一块的内存用完时,将存活的对象分配到另一块的空间上,然后将已使用过的内存空间一次性清理掉,这样使得每次都是对半个区域进行回收,内存分配也不需要考虑内存碎片,只要移动端指针就可以了,按照顺序分配,运行高效;缺点是造成一部分的内存空间浪费。

标记-清除算法:标记所有要回收的对象,标记完成之后统一回收。主要缺点是算法效率低,会造成不连续的空间,当存储较大的对象的时候,会造成提前进行垃圾回收。

标记-整理算法:针对年老代的特点,提出了标记-整理的算法,基本过程仍然与标记清除算法一样,后序不是直接对可回收的对象进行清除,而是让所有存活的对象向一端移动,然后直接清理边界以外的内存。

触发GC的条件:
1、当前应用程序空闲,GC运行在优先级最低的线程中,当没有程序运行时,GC才会运行
2、内存不足时,GC会被强制调用。如果一次不够之后会继续一次GC,两次都不够,会直接报OOM
3、代码调用了System.gc(),但是不一定会调用系统GC,只是提醒系统这时候可以GC

减少GC开销的措施:
1、少调用System.gc()
2、减少临时对象的调用,临时对象退出后置为null
3、对象使用完后,设置为null
4、尽量使用int等基本类型,少用Integer等引用类型
5、少使用static变量
6、字符串长度变换的使用StringBuffer,不用String
7、分散创建和删除对象,一次性太多容易OOM

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值