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

前言

之前在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 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 refQueue = new ReferenceQueue();
        PhantomReference 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() 返回了不为空对象,即没有回收

软引用、弱引用和虚引用处理_第1张图片

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

软引用、弱引用和虚引用处理_第2张图片

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

软引用、弱引用和虚引用处理_第3张图片

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

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

软引用、弱引用和虚引用处理_第4张图片

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

软引用、弱引用和虚引用处理_第5张图片

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


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)

运行结果:

软引用、弱引用和虚引用处理_第6张图片

Done

你可能感兴趣的:(Java,Android,Kotlin)