最近遇到了一个问题,重新理解整理(水)下变量与对象的关系和弱引用。
先抛出一个问题
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=小明)
如果你的回答错误,那得整明白了这件事才可以继续往下走🏃🏻♀️。
我们根据代码一步步梳理下来
- 创建一个对象Person,然后使用变量p进行引用
- 创建一个对象Wrapper并且把对象Person传给构造函数,然后使用变量wrapper进行引用
- 将p的引用置为null
- 调用wrapper对象的print方法
- print方法打印Person对象
⚠️需要特别注意粗体字
在JVM内存模型里面大致是这样的
在步骤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(垃圾回收机制)。
让我们整理下 这两段代码。
变量p对Person是强引用,变量weak对Person是弱引用
切断p对Person的引用
一旦发生gc,马上会回收Person对象,因为Person目前只有一个软引用持有着,并没有任何强引用连向GCRoot,所以会被回收♻️。谁可以作为GCRoot可以看这篇文档,里面把所以可成为GCRoot都做了介绍。
到此文章结束
- 理解java变量和对象之间的关系。
- 如果使用弱引用,一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的。