定义

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器宁愿抛出OOM(OutOfMemoryError)也不会回收它。

说明

不要被这个字吓到,以为这个引用就很厉害,其实强引用就是程序中使用的一般引用类型。举个简单的栗子:

1
String s = new String("Hello Frank!");

::: tip 强可达
如果一个对象与GC Roots之间存在强引用,则称这个对象为强可达(strong reachable)对象。
:::

关于可达性如果不太清楚的话,可以翻阅这篇文章

当你声明一个变量并指向一个实例的时候,其实就是在创造一个强引用。那么,既然叫强引用,它“强”在哪里呢?

这主要体现在JVM进行GC的时候,只要对象有强引用与其关联,就绝对不会对它进行回收,即使已经内存不足了也不会收回有强引用指向的对象。

::: tip 强引用独白
这是我小弟,有我罩着,垃圾回收器你别动它。
:::

如果你不需要使用某个对象了,可以将相应的引用设置为null,消除强引用来帮助垃圾回收器进行回收。因为过多的强引用也是导致OOM的罪魁祸首。

1
s = null;

显式地设置消除引用,或已超出对象的生命周期范围,则JVM会认为该对象不存在引用,这时就可能会回收这个对象。但是具体什么时候收集这要取决于具体的GC算法。

如果在一个方法的内部有一个变量s持有一个对象(Object)的强引用,那么这个变量s保存在栈中,而真正的引用内容(object)保存在堆中。当这个方法运行完成后就会退出方法栈,则引用s也会被销毁,这个object就可能会在之后的一次GC中回收。但是当这个s是全局变量时,就需要在不再使用这个对象时将引用s赋值为null,也就是消除与object对象之间的强引用,因为有强引用关联的对象是不会被垃圾回收的。

下面看另一个🌰:

1
2
3
A a = new A();
B b = new B(a);
a = null;

这里a和b都持有一个对象的强引用,当执行 a = null 时, a 不再持有 A 的强引用。讲道理,A 已经该被回收了。但是这里a = null 时,A 对象不满足被回收的条件,因为还有一个B对象持有其强引用,这时候就会造成内存泄漏

再看另一个会导致内存泄漏的栗子:

1
2
3
4
5
public static ArrayList<Object> list = new ArrayList<Object>();
public void stackOverflowTest(Object object){
list.add(object);
object = null;
}

GC回收的是不可达对象,但是,在这个静态集合类对象中,持有了对象的强引用,但却有可能其中的某些对象已经不再使用了,所以当非静态对象被静态变量持有强引用的时候,最容易发生内存泄露。

在方法中从list获取到对象后赋值给一个变量,使用完之后将这个变量设置为null并不会释放object引用的对象,因为list中还是持有对象的强引用。这时就造成了内存泄漏

小结

所以小结一下强引用的特点:

  • 强引用就是最普通的引用
  • 可以使用强引用直接访问目标对象
  • 强引用指向的对象在任何时候都不会被系统回收
  • 强引用可能会导致内存泄漏
  • 过多的强引用会导致OOM