java中强软弱虚引用

一个自定义类

1
2
3
4
5
6
public class M {
@Override
protected void finalize() throws Throwable {
System.out.println("finalize");
}
}

finalize() 方法,在该对象要被垃圾回收的时候被调用

强引用

  • 平常所用的那些普通的引用,如B a=new B()
  • 当有强引用指向某对象时,该引用一定不会被垃圾回收
  • 当内存空间不足的时候,java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会随意回收具有强引用的对象来解决内存不足的问题。
  • 可以使用a = null的方式切断强引用
1
2
3
4
5
6
7
8
9
public class DemoNormalReference {
public static void main(String[] args) throws IOException {
M m = new M();
m = null;
System.gc(); //显式地触发full GC

System.in.read(); //阻塞主线程,保证能看到垃圾回收线程(后台线程)对M的回收
}
}

System.gc() 调用附带一个免责声明,无法保证对垃圾收集器的调用。

但是这里最好写上,大概率是可以调用的。如果不写,在内存空闲较多的情况下,会一直等待,看不到效果。

软引用

  • 如果内存空间足够,垃圾回收器就不会回收软引用所指向的对象;如果空间不足了,就会回收这些对象的内存。
  • 只要垃圾回收器没有回收它,该对象就可以被程序使用。
  • 软引用可以用来实现缓存。内存空余,使用缓存;内存不足,释放缓存。
  • 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,java虚拟机就会把这个软引用对象加入到与之关联的引用队列中
  • 最后来讲:软引用是一个对象,它所引用的对象被认为被软引用指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 为了演示出效果,需加上虚拟机参数
*/
public class DemoSoftReference {
public static void main(String[] args) {
// 软引用指向一个 10M的缓存
SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
System.out.println(m.get());
System.gc();
try{
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(m.get());

// 再分配一个数组,堆装不下,就会释放缓存
byte[] b = new byte[1024*1024*15]; // 15M
System.out.println(m.get());
}
}

虚拟机参数设定

结果展示

弱引用

  • 如果一个对象只有弱引用,那么在垃圾回收的时候它会被立即回收。

  • 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,java虚拟机就会把这个弱引用对象加入到与之关联的引用队列中。

1
2
3
4
5
6
7
8
public class DemoWeakReference {
public static void main(String[] args) {
WeakReference<M> m = new WeakReference<>(new M());
System.out.println(m.get());
System.gc();
System.out.println(m.get());
}
}

用途:在某些场合,常用来使虚拟机在某些时候明确其是垃圾【废话】

借鉴网上博主一段话,【此段自己已经理解,保证准确性】:


考虑下面的场景:现在有一个Product类代表一种产品,这个类被设计为不可扩展的,而此时我们想要为每个产品增加一个编号。一种解决方案是使用HashMap<Product, Integer>。于是问题来了,如果我们已经不再需要一个Product对象存在于内存中(比如已经卖出了这件产品),假设指向它的引用为productA,我们这时会给productA赋值为null,然而这时productA过去指向的Product对象并不会被回收,因为它显然还被HashMap引用着。所以这种情况下,我们想要真正的回收一个Product对象,仅仅把它的强引用赋值为null是不够的,还要把相应的条目从HashMap中移除。显然“从HashMap中移除不再需要的条目”这个工作我们不想自己完成,我们希望告诉垃圾收集器:在只有HashMap中的key在引用着Product对象的情况下,就可以回收相应Product对象了。显然,根据前面弱引用的定义,使用弱引用能帮助我们达成这个目的。我们只需要用一个指向Product对象的弱引用对象来作为HashMap中的key就可以了。


摘自——-理解java中的弱引用

如上的论述在ThreadLocal的源码中也有清晰的体现,有效防止了内存的泄漏

虚引用

  • 虚引用根本就不像是一个引用,无论什么情况get()方法的返回值都是一个null
  • 如果一个对象只有虚引用指向它了,那么它一定会被垃圾回收
  • 它最好和ReferenceQueue关联(不然不知道其有什么用),当其引用的对象被垃圾回收,其自身被加入到引用队列中,从这个队列中我们就可以得知,哪些虚引用指向的对象被垃圾回收了
  • 主要用途是用于检测对象是否已经从内存中删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class DemoPhantomReference {
private static final List<Object> LIST = new LinkedList<>();
private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();

public static void main(String[] args){
PhantomReference<M> phantomReference = new PhantomReference<>(new M(),QUEUE);

new Thread(()->{
while(true){
LIST.add(new byte[1024*1024*10]);
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
System.out.println(phantomReference.get());
}
}).start();

new Thread(()->{
while(true){
Reference<? extends M> poll = QUEUE.poll();
if(poll != null){
System.out.println("--- 虚引用对象所引用的对象被jvm回收了 ----" + poll);
}
}
}).start();
}
}

总结

总之,合理的使用引用可以帮助垃圾回收器更好的管理Java内存。


java中强软弱虚引用
https://blog.wangxk.cc/2020/10/07/java中强软弱虚引用/
作者
Mike
发布于
2020年10月7日
许可协议