重新理解变量与对象的关系

425 阅读3分钟

最近遇到了一个问题,重新理解整理(水)下变量与对象的关系和弱引用。

先抛出一个问题

data class Person(var name:String)

fun main() {
    var p: Person? = Person("小明")
    val wrapper = Wrapper(p)
    p = null
    wrapper.print()
}

class Wrapper(val any: Person?){
    fun print(){
        println(any)
    }
}

输出结果是什么?

1

2

3

.....

答案是:Person(name=小明)

如果你的回答错误,那得整明白了这件事才可以继续往下走🏃🏻‍♀️。

我们根据代码一步步梳理下来

  1. 创建一个对象Person,然后使用变量p进行引用
  2. 创建一个对象Wrapper并且把对象Person传给构造函数,然后使用变量wrapper进行引用
  3. 将p的引用置为null
  4. 调用wrapper对象的print方法
  5. print方法打印Person对象

⚠️需要特别注意粗体字

在JVM内存模型里面大致是这样的

image.png

在步骤2中Wrapper也引用了Person对象,也就是持有了该对象。

在步骤3中将p引用置为null,注意这里是将p的引用置为null,并不是将Person这个对象销毁,Person对象任存活在堆中,而Wrapper对象持有的Person不受任何影响。这也是为什么print方法可以正常打印的原因。


有了上面的铺垫,我们继续往下 👇🏻

fun main() {
    var p: Person? = Person("小明")
    val weak = WeakReference(p)
    p = null
    println(weak.get()==null)
}

上面的输入结果是什么?👆🏻

1

2

3

....

答案是:false

这次回答对了吗?

这次的代码和第一段的代码的区别就是把Wrapper换成了WeakRefence(弱引用)。通过上面铺垫很容易理解正确答案。

维基百科

弱引用:在计算机程序设计中,弱引用强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此可能在任何时刻被回收。

要注意上面的粗体字👆🏻

然后我们再来看一段代码👇🏻

fun main() {
    var p: Person? = Person("小明")
    val weak = WeakReference(p)
    p = null
    System.gc()
    println(weak.get()==null)
}

上面的输入结果是什么?👆🏻

1

2

3

....

答案是:true

与第二段代码的差别只有调用了GC(垃圾回收机制)。

让我们整理下 这两段代码。

image.png

变量p对Person是强引用,变量weak对Person是弱引用

image.png

切断p对Person的引用

image.png

一旦发生gc,马上会回收Person对象,因为Person目前只有一个软引用持有着,并没有任何强引用连向GCRoot,所以会被回收♻️。谁可以作为GCRoot可以看这篇文档,里面把所以可成为GCRoot都做了介绍。

到此文章结束

  1. 理解java变量和对象之间的关系。
  2. 如果使用弱引用,一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的