软引用、弱引用和虚引用处理

17 篇文章 0 订阅
7 篇文章 0 订阅

前言

之前在Android上使用 Handler 引起了内存泄漏。从而认识了弱引用、软引用、虚引用。今天发现Kotlin 在Android 上Anko库里的async, uiThread 里面居然做了在异步执行过程中Activity销毁了uiThread则不会调用,防止内存泄漏。正是采用了弱引用,先温习一下。


Java中有如下四种类型的引用:

  • 强引用(Strong Reference)
  • 软引用(SoftReference)
  • 弱引用(WeakReference)
  • 虚引用(PhantomReference)

Java.lang.ref 是 Java 类库中的一个包,它提供了与 Java 垃圾回收器密切相关的引用类。这些引用类对象可以指向其它对象,但它们不同于一般的引用,因为它们的存在并不防碍 Java 垃圾回收器对它们所指向的对象进行回收。其好处就在于使者可以保持对使用对象的引用,同时 JVM 依然可以在内存不够用的时候对使用对象进行回收。因此这个包在用来实现与缓存相关的应用时特别有用。同时该包也提供了在对象的“可达”性发生改变时,进行提醒的机制。

强引用(Strong Reference)

强引用就是我们经常使用的对象引用。 在Java中没有相应的类与它对应 ,如果一个对象属于强引用,就算内存不足时,JVM情愿抛出OOM异常使程序异常终止也不会靠回收强引用的对象来解决内存不足的问题。

软引用(Soft Reference)

SoftReference 在“弱引用”中属于最强的引用。SoftReference 所指向的对象,当没有强引用指向它时,会在内存中停留一段的时间,垃圾回收器会根据 JVM 内存的使用情况(内存的紧缺程度)以及 SoftReference 的 get() 方法的调用情况来决定是否对其进行回收。

弱引用(Weak Reference)

WeakReference 是弱于 SoftReference 的引用类型。弱引用的特性和基本与软引用相似,区别就在于弱引用所指向的对象只要进行系统垃圾回收,不管内存使用情况如何,永远对其进行回收(get() 方法返回 null)。

虚引用 (Phantom Reference)

PhantomReference 是所有“弱引用”中最弱的引用类型。不同于软引用和弱引用,虚引用无法通过 get() 方法来取得目标对象的强引用从而使用目标对象,观察源码可以发现 get() 被重写为永远返回 null。

引用对列 (Reference Queue)

在适当的时候检测到对象的可达性发生改变后,垃圾回收器就将已注册的引用对象添加到此队列中。一旦弱引用对象开始返回null,该弱引用指向的对象就被标记成了垃圾。而这个弱引用对象(非其指向的对象)就没有什么用了。通常这时候需要进行一些清理工作。比如WeakHashMap会在这时候移除没用的条目来避免保存无限制增长的没有意义的弱引用。

引用类型特性总结

引用类型取得目标对象方式垃圾回收条件是否可能内存泄漏
强引用直接调用不回收可能
软引用通过 get() 方法视内存情况回收不可能
弱引用通过 get() 方法永远回收不可能
虚引用无法取得不回收可能

理论到实践->Java代码

public class ReferenceDemo {
    private List<Object> list = new ArrayList<>();

    public static void main(String[] args) {
        ReferenceDemo referenceDemo = new ReferenceDemo();
        //referenceDemo.strongReference();

        referenceDemo.softReference();
        referenceDemo.weakReference();
        referenceDemo.phantomReference();
    }

    private void phantomReference() {
        System.out.println("------phantomReference--------");
        ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
        PhantomReference<Object> referent = new PhantomReference<>(new Object(), refQueue);
        System.out.println(referent.get());
        System.gc();
        System.runFinalization();
        System.out.println(String.format("is recycle %s", (refQueue.poll()) == referent));
        System.out.println("------phantomReference--------");
    }

    private void weakReference() {
        list.clear();
        System.out.println("------WeakReference--------");
        for (int i = 0; i < 2; i++) {
            byte[] buff = new byte[1024 * 1024];
            WeakReference weakReference = new WeakReference(buff);
            list.add(weakReference);
        }
        list.forEach(o -> {
            Object o1 = ((WeakReference) o).get();
            System.out.println(o1);
        });
        System.out.println("------WeakReference--------");
    }

    private void strongReference() {
        byte[] buff = new byte[1024 * 1024 * 5];
    }

    private void softReference() {
        System.out.println("------softReference--------");
        for (int i = 0; i < 2; i++) {
            byte[] buff = new byte[1024 * 1024];
            //list.add(buff)
            SoftReference softReference = new SoftReference(buff);
            list.add(softReference);
        }
        list.forEach(o -> {
            Object o1 = ((SoftReference) o).get();
            System.out.println(o1);
        });
        System.out.println("------softReference--------");
    }
}

运行以上代码不出意料,内存充足时 软、弱引用 get() 返回了不为空对象,即没有回收

这里写图片描述

接下来在intellij idea配置jvm内存 -Xmn3m -Xmx5m 再运行

这里写图片描述

执行 byte[] buff = new byte[1024 * 1024 * 5];

这里写图片描述

由上确实是JVM情愿抛出OOM异常使程序异常终止也不会靠回收强引用的对象

接下看看软、弱、虚引用情况

这里写图片描述

还没看出结果?把循环+1

这里写图片描述

所以得出了结论: 软引用在内存紧缺时,会回收对象,而弱引用只要进行系统垃圾回收,都会回收对象。


koltlin 是基于jvm 的肯定也存在内存泄漏问题->Kotlin代码

fun main(args: Array<String>) {
    for (index in 1..3) {
        Ref().testWeakReference()
    }
}

class Ref {
    val b = ByteArray(1024 * 1024)
    fun testWeakReference() {
        Thread({
            Thread.sleep(300)
            println(b)
        }).start()
    }
}

小伙伴跑一下代码,便会发现OOM了,当然你还是要配置jvm堆内存 栈内存大小,尽量设小点,我这里依然是 -Xmn3m -Xmx5m
发生了OOM 就是 println(b) 强引用着 b 对象。既然Anko库里的async, uiThread 里面能防止内存泄漏。那我也来搞搞吧!

class Ref {
    val b = ByteArray(1024 * 1024)
    fun testWeakReference() {
        val weakReference = this@Ref.weakReference()
        Thread({
            Thread.sleep(300)
            println(weakReference()?.b)
        }).start()
    }
}


class KWeakReference<T> internal constructor(any: T) {
    private val weakRef = WeakReference<T>(any)

    operator fun invoke(): T? {
        return weakRef.get()
    }
}

internal fun <T> T.weakReference() = KWeakReference(this)

运行结果:

这里写图片描述

Done

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值