强引用、软引用、弱引用

转自[http://allenfeng.com/2016/12/07/understanding-how-references-work-in-android-and-java]

引用的强度: 强引用 > 软引用 > 弱引用 > 虚引用。

Android中的对象有着4种引用类型,垃圾回收器对于不同的引用类型有着不同的处理方式,了解这些处理方式有助于我们避免写出会导致内存泄露的代码。

在Java中,一切都被视为对象,引用则是用来操纵对象的途径。

对象和引用之间的关系可以用遥控器(引用)来操纵电视机(对象)这个场景来理解。只要手持这个遥控器,就能保持与电视机的连接。当我们想要改变频道或者音量时,实际操控的是遥控器(引用),再由遥控器(引用)来调控电视机(对象),达到操控的目的。

当我们尝试在一个未指向任何对象的引用上去操作对象时,就会遇到经典的空指针异常(NullPointerException)。可以理解成我们手持遥控器,房间里却没有电视机可与之对象(没有可以用来操控的对象)

GC与内存泄露

Java的一个重要优点就是通过垃圾收集器(Garbage Collection,GC)自动管理内存的回收,开发者不需要通过调用函数来释放内存。在Java中,内存的分配是由程序分配的,而内存的回收是由GC来完成。
GC为了能够正确释放对象,会监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。

Android中采用了标注与清理(Mark and Sweep)回收算法:
从”GC Roots”集合开始,将内存整个遍历一次,保留所有可以被GC Roots直接或间接引用到的对象,而剩下的对象都当作垃圾对待并回收。

结合上文所述,内存泄露指的是:

我们不再需要的对象资源仍然与GC Roots存在可达路径,导致该资源无法被GC回收。

Android中的对象有着4种引用类型,垃圾回收器对于不同的引用类型有着不同的处理方式,了解这些处理方式有助于我们避免写出会导致内存泄露的代码。

Strong reference(强引用)

强引用我们最常用的一种引用类型。当我们使用new关键字去新建一个对象的时候,创建的就是强引用。

当一个对象具有强引用,那么垃圾回收器是绝对不会的回收和销毁它的。对象的强引用可以在程序中到处传递。很多情况下,会同时有多个引用指向同一个对象。

强引用的存在限制了对象在内存中的存活时间。假如对象A中包含了一个对象B的强引用,那么一般情况下,对象B的存活时间就不会短于对象A。如果对象A没有显式的把对象B的引用设为null的话,就只有当对象A被垃圾回收之后,对象B才不再有引用指向它,才可能获得被垃圾回收的机会。

在Java中,非静态内部类会在其整个生命周期中持有对它外部类的强引用

WeakReference (弱引用)

弱引用通过类WeakReference来表示。弱引用并不能阻止垃圾回收。如果使用一个强引用的话,只要该引用存在,那么被引用的对象是不能被回收的。弱引用则没有这个问题。在垃圾回收器运行的时候,如果对一个对象的所有引用都是弱引用的话,该对象会被回收。

需要注意的是,GC回收的是对象,在垃圾回收器运行的时候,如果对一个对象的所有引用都是弱引用的话,该对象会被回收。

SoftReference(软引用)

我们可以把软引用理解成一种稍强的弱引用。使用类SoftReference来表示。

很多人可能会把弱引用和软引用搞混,注意他们的区别在于:如果一个对象只具有软引用,若内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,才会回收这些对象的内存。

而只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

表面上看来,软引用非常适合于创建缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。

但是,在实践中,使用软引用作为缓存时效率是比较低的,系统并不知道哪些软引用指向的对象应该被回收,哪些应该被保留。过早被回收的对象会导致不必要的工作,比如Bitmap要重新从SdCard或者网络上加载到内存。

所以使用软引用去缓存对象,虽然确实可以避免OOM问题,却不适用于某些场景。在Android开发中,一种更好的选择是使用LruCache,LRU是Least Recently Used的缩写,即“最近最少使用”,它的内部会维护一个固定大小的内存,当内存不足的时候,会根据策略把最近最少使用的数据移除,让出内存给最新的数据。具体实现有兴趣的同学可以自行研究。

PhantomReference(虚引用)

一个只被虚引用持有的对象可能会在任何时候被GC回收。虚引用对对象的生存周期完全没有影响,也无法通过虚引用来获取对象实例,仅仅能在对象被回收时,得到一个系统通知(只能通过是否被加入到ReferenceQueue来判断是否被GC,这也是唯一判断对象是否被GC的途径)。

我们都知道,java的Object类里面有个finalize方法,它的工作原理是这样的:一旦垃圾回收器准备好释放对象占用的内存空间,将首先调用其finalize方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。但是,问题在于,虚拟机不能保证finalize何时被调用,因为GC的运行时间是不固定的。

使用虚引用就可以解决这个问题,虚引用主要用来跟踪对象被垃圾回收的活动,主要用来实现比较精细的内存使用控制,这对于Android设备来说是很有意义的。比如,我们可以在确定一个Bitmap被回收后,再去申请另外一个Bitmap的内存,通过这种方式可以使得程序所消耗的内存维持在一个相对较低且稳定的水平。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值