WeakReference监听GC与误区

/ 0评 / 1

WeakReference

在文章https://27house.cn/archives/2079中介绍了4种引用类型,其中WeakReference可以与ReferenceQueue来判断对象是否回收。那么其实我们可以使用此方法来监听系统是否进行了GC

WeakReference监听GC

如下所示,通过添加哨兵对象,当触发GC的时候,哨兵对象会被回收,从而获取GC

class SentinelObj

class GcMonitor {
    private val TAG = "GcMonitor"
    private val queue = ReferenceQueue<Any>()
    private var weakReference: WeakReference<Any>? = null
    private var thread: Thread? = null

    fun startMonitor(call: () -> Unit) {
        thread = thread(name = "GcMonitor") {
            while (!Thread.currentThread().isInterrupted) {
                Log.i(TAG, "开始监听GC")
                val result = kotlin.runCatching {
                    queue.remove()
                }
                if (result.isSuccess) {
                    call.invoke()
                    addSentinelObj()
                } else {
                    Thread.currentThread().interrupt()
                }
            }
            Log.i(TAG, "GcMonitor 退出")
        }
        addSentinelObj()
    }

    fun stop() {
        thread?.interrupt()
    }

    private fun addSentinelObj() {
        Log.i(TAG, "添加哨兵")
        weakReference = WeakReference(SentinelObj(), queue)
    }
}

finalize与对象回收

要监听对象是否回收,除了使用WeakReference与ReferenceQueue,还可以使用Java Object对象中的finaliz方法。如下所示

public class SentinelObj extends Object {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        Log.i("GcMonitor", "finalize!!!");
    }
}

番外:如何触发GC

通过系统方法Runtime.getRuntime().gc()与Runtime.getRuntime().runFinalization()只能建议系统进行GC,但是无法保证一定会GC

public void triggerGc() {
    long current = System.currentTimeMillis();
    if (mDumpHprofMode == ResourceConfig.DumpMode.NO_DUMP
            && current - lastTriggeredTime < getResourcePlugin().getConfig().getScanIntervalMillis() / 2 - 100) {
        MatrixLog.v(TAG, "skip triggering gc for frequency");
        return;
    }
    lastTriggeredTime = current;
    MatrixLog.v(TAG, "triggering gc...");
    Runtime.getRuntime().gc();
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        MatrixLog.printErrStackTrace(TAG, e, "");
    }
    Runtime.getRuntime().runFinalization();
    MatrixLog.v(TAG, "gc was triggered.");
}

使用adb

> adb shell
> adb run-as package-name   //获取权限
> ps -A | grep package-name //获取pid
> kill -10 pid                      //强制触发GC

番外:对象一定会被回收么?

private var weakReference: WeakReference<Any>? = null
private val queue = ReferenceQueue<Any>()
var thread: Thread? = null

fun start() {
    thread = thread {
        weakReference = WeakReference(SentinelObj(), queue)

        //👇🏻让线程存活
        while (!Thread.currentThread().isInterrupted) {
            try {
                Thread.sleep(100)
            } catch (e: Exception) {
                Thread.currentThread().interrupt()
            }
        }

    }
}

fun stop() {
    thread?.interrupt()
}

当我们调用start,将SentinelObj对象使用WeakReference引用,在线程停止前,虽然SentinelObj对象只有一个弱应用,但是GC是不会回收此对象!!!因为在栈中还被引用了。所以我们只需要把代码改到一个单独函数中即可,如前文GcMonitor中的addSentinelObj(),就是为了规避GcMonitor Thread的栈引用SentinelObj,避免GC的回调不会被执行!!!!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注