Android垃圾回收机制

本文详细解析了Java垃圾回收的核心思想,包括垃圾回收的概念、引用计数GC处理、引用遍历GC处理以及如何避免内存溢出的问题。通过实例解释了循环引用的处理方式和GCRooter的作用,提供了优化内存使用的建议。

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

最近跟一位同学聊天,在听他说起找工作的事情时,问了问面试的一些题目,其中GC算法核心没怎么接触,于是自己找了下书本,查了查,总结如下:


垃圾回收是什么

其实垃圾回收(Garbage Collector)是指追踪堆内存(Heap)中正在使用的对象,同时发现并回收没有任何引用指向的对象以及整理记忆碎片的一种过程。

前两个很好理解,但是什么是记忆碎片呢?大家可以想象下,由于创建对象以及垃圾回收时回收对象的频繁调用,会导致出现断断续续的存储状况   “一  一一       一    一”,中间空余的区域如果太小,就不能再为新对象分配合理空间,内存回收时可以对记忆碎片进行处理  “一一一             ”。

通常触发垃圾回收的情况有三种:1.没有足够的内存分配新请求的内存时,2.分配完内存后,堆内存剩下的内存达到或者超过一个阈值时,3.退出或者进入新的应用

垃圾回收核心思想

引用计数GC处理


对虚拟机可用的内存空间,也就是堆内存里面的对象进行标记,每个对象有一个计数位用来记录指向它的引用,如果有一个对象引用它,那么计数位的值为1,每当增加或者减少,计数位都会相应的加减1,直到为0。
如果计数位不为0,则标记此对象为存活对象,如果为0,则被标记为垃圾对象,可以对其进行回收。
但上述方法并不能对循环引用的两个对象进行辨别

什么是循环引用

class Node {
  Node next;
}

Node a = new Node();
Node b = new Node();
a.next = b;
b.next = a;

其中a指向b,b指向a导致两个对象互相引用成为一个循环引用。不管怎么释放,两个对象的引用计数总为1,所以不能被标记为垃圾对象,将一直占据空间。
留着这样的对象在内存肯定不行,程序用久了指定会造成内存溢出,对于这种状况,android造就采取其他方式解决了。
就是接下来将要介绍的引用遍历GC处理。

引用遍历GC处理

垃圾回收器从GC Rooter的这个点,开始对堆内存中的对象进行遍历,凡是到达的点,都被标记为存活点,遍历完毕后,未到达的点,则被标记为垃圾回收点,处于这些点的对象都将被回收。

那么有人便问了,GC Rooter这个点,到底是什么东西呢?下面对其进行了总结:
  • 类,由android系统类加载器(DexClassLoader)加载的类。由于android系统类加载器加载的类从来不回被卸载,它们以静态属性的方式持有由其创建的对象的引用。注意,如果用户自定义了一个类加载器,那么它加载的类不能被称为GC Rooter。
  • 线程,存活的线程。
  • java方法栈中的局部变量或者参数
  • JNI全局引用
  • 被JVM持有的对象,这些对象由于特殊的目的不被GC回收。这些对象可能是系统的类加载器,一些重要的异常处理类,一些为处理异常预留的对象,以及一些正在执行类加载的自定义的类加载器。但是具体有哪些前面提到的对象依赖于具体的JVM实现。

在Android平台中,如果对一个资源保持长期的引用,忘记释放,则很容易造成内存溢出。为了保证应用的性能问题,下面的建议或许能对大家起一点帮助。
  • Context 不要声明为static类型。
  • 尽量不要使用非静态内部类。我们都知道,内部静态类会持有外部类的引用,如果内部类存在异步耗时任务,当退出该Activity后,假设该任务依旧运行,那么外部类的内存将不会得到释放,容易引起内存溢出。
  • Bitmap的慎用。在列表中尽量使用缩略图,在展示图片的时候避免使用ARGB_8888这种高质量的像素展示,尽量选择RGB_565。用完记得回收。
  • 强引用,软引用,弱引用的使用在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术,例如ActivityManager常用的Activity管理,尽量使用软引用。
  • static final 修饰常量。如果对常量不这样使用,在程序编译时,编译器会生成一个clinit的初始化类的方法,它会把(static str = “hello,wen!”)中的“hello,wen!”赋值给str,并将其加入类的成员变量表,以后每次查询的时候,都需要对成员变量表进行遍历。而实用static final时则会直接将其保存到类文件中,不需要生成clinit方法。
  • 避免创建不必要的对象。比如我们常会用的一种方法String str = "hello"+"wen"+"!";这种方式容易造成多个String对象的创建,对内存造成很大的压力。不如尝试使用StringBuilder。
  • for循环,第二条件中不要使用方法。for(int i=0;i<array.lengh();i++),这样写是不合适的,每次循环都会重新获取一次array.length()。int l = array.length();for(int i = 0;i<l;i++)这样写比较好。
  • 复写Adapter时对convertView的重复使用。这个建议大家搜一下ListView的优化。
  • TypedArray,Cursor等android耗内存的工具用完后记得回收
  • 避免在主线程中启动耗时任务。考虑多线程的使用。
  • 能用int,long就不要随意使用Integer,Long等




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值