Java强引用、软引用、弱引用、虚引用

  • 强引用

    强引用和其他引用不同的是它不需要外部对象去包含实际的对象,比如软引用会使用SoftReference包含。

    当强引用对象设置为null时,即它没有指向任何的对象了,或者超出他的生命周期了(比如方法体中的局部变量,在方法结束后该变量的生命周期就结束了),此时强引用对象是可以被gc回收的,只不过回收的时机不确定,取决于gc算法。

    如果强引用在未超出生命周期的前提下一直指向某个地方,则它永远不会被gc回收,甚至出现OutOfMemory。

  • 软引用

    /**
     * 软引用
     * 内存不足的时候才会回收
     */
    private fun testSoftReference() {
        var obj: Any? = Any()
        val refQueue = ReferenceQueue()
        val softRef = SoftReference(obj, refQueue)
        //null后gc才会扫描到需要回收
        obj = null
        println("Before GC: " + softRef.get())
        println("Before GC queue: " + refQueue.poll())
        System.gc()
        Thread.sleep(3000)
        println("After GC: " + softRef.get())
        println("After GC queue: " + refQueue.poll())
    }
    

    软引用通过SoftReference构造,第一个参数是实际的对象,第二个参数是可选的,可以设置一个ReferenceQueue,当软引用被回收后就会被添加到这个ReferenceQueue中。

    软引用的对象只有在内存不足时才会被回收,在内存充足时哪怕我们把对象设置为null然后调用System.gc()后也不会被回收,这时以上的代码输出结果为:

    Before GC: java.lang.Object@117159c0
    Before GC queue: null
    After GC: java.lang.Object@117159c0
    After GC queue: null
    

    软引用适合当内存充足的情况下尽可能的保留对象,当内存不足的时候又允许被回收而不会造成崩溃的情况,比如一个浏览器应用一般都有回到上一个网页的功能,所以在打开新页面的时候上一个页面的内容通常也需要保留在内存中方便返回,但是在内存不足的情况下肯定要优先保留当前页面的内容,此时上一个页面的内容就允许被回收,大不了返回的时候判断被回收了的话就重新加载,这时使用软引用就很合适,也不会有OutOfMemory风险。

  • 弱引用

    /**
     * 弱引用
     * gc扫描到就会回收(两次扫描)
     */
    private fun testWeakReference() {
        val refQueue = ReferenceQueue()
        //1.这里弱引用关联的对象没有任何强引用
          //val weakRef = WeakReference(Any(), refQueue)
        //2.关联强引用
        var obj: Any? = Any()
        val weakRef = WeakReference(obj, refQueue)
        //这里需要解除强引用之后才会被回收
        obj = null
    
        println("Before GC: " + weakRef.get())
        println("Before GC queue: " + refQueue.poll())
        System.gc()
        //给一定的时间完成回收操作
        Thread.sleep(6000)
        println("After GC: " + weakRef.get())
        println("After GC queue: " + refQueue.poll())
    }
    

    弱引用通过WeakReference构造,第一个参数是实际的对象,第二个参数也是可选的,可以设置一个ReferenceQueue,当弱引用被回收后就会被添加到这个ReferenceQueue中。

    和软引用不同的是,弱引用的对象一旦被gc扫描到后就会被回收,即使是在内存充足的情况下也是如此,前提是它关联的对象没有强引用,比如上面的第二种方式,obj是有强引用的,此时就需要obj设置为null后才会被回收。

    以上代码的输出结果为:

    Before GC: java.lang.Object@117159c0
    Before GC queue: null
    After GC: null
    After GC queue: java.lang.ref.WeakReference@53045c6c
    

    强引用时未设置为null后的输出结果为:

    Before GC: java.lang.Object@117159c0
    Before GC queue: null
    After GC: java.lang.Object@117159c0
    After GC queue: null
    

    弱引用适合一些创建成本不是很大的对象,即当判断weakRef.get()为null后可以轻松再次创建的情况。

  • 虚引用

    /**
     * 虚引用
     * 等于无引用,回收后会放到ReferenceQueue,以此来用于跟踪gc流程
     */
    private fun testPhantomReference() {
        var obj: Any? = Any()
        val refQueue = ReferenceQueue()
        val phantomRef = PhantomReference(obj, refQueue)
        //null后gc才会扫描到需要回收
        obj = null
        println("Before GC: " + phantomRef.get())
        println("Before GC queue: " + refQueue.poll())
        System.gc()
        Thread.sleep(3000)
        println("After GC: " + phantomRef.get())
        println("After GC queue: " + refQueue.poll())
    }
    

    虚引用通过PhantomReference(Phantom读作ˈfæntəm)构造,不同的是,他只有两个参数的构造方法,第一个参数是实际的对象,第二个参数是必选的,可以设置一个ReferenceQueue,当虚引用被回收后就会被添加到这个ReferenceQueue中。

    和弱引用一样,gc一旦扫描到虚引用就会立即回收它关联的对象,不同的是,虚引用关联的对象无法通过get方法获取到,它的作用只是用来跟踪gc回收(通过ReferenceQueue的poll方法判断是否有值)。

    以上代码的输出结果为:

    Before GC: null
    Before GC queue: null
    After GC: null
    After GC queue: java.lang.ref.PhantomReference@795509d9
    

你可能感兴趣的:(Java强引用、软引用、弱引用、虚引用)