18780334870 发表于 2021-1-11 10:22

java中的引用类型

Java中4种引用的级别由高到低依次为:
强引用>软引用>弱引用>虚引用

class M {
    @Override
    protected void finalize() throws Throwable {
      System.out.println("finalize");
    }
}


强引用:
(只有指定m = null;时才进行回收)
// 下列代码就是一个强引用
M m = new M();



软引用
(作用:非常适合缓存使用)
// 运行下面代码的时候需要指定命令行参数(堆最大值20M):-Xmx20M
// 下面的代码会分配一个10M的空间,然后前两次get()都可以正常获取到
// 然后实例化一个强引用的15M的数组,这个时候就会内存泄漏
SoftReference<byte[]> m = new SoftReference<>(new byte);
System.out.println(m.get());
System.gc();
try {
    Thread.sleep(500);
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println(m.get());
// 再分配一个数组,heap将装不下,这时候系统会垃圾回收(启动的时候java会占用一部分内存),先回收一次,如果不够,会把软引用干掉
byte[] b = new byte;
System.out.println(m.get());



弱引用
(结论:只要垃圾回收器看到了就直接回收,与软引用区别:软引用是内存不够了才进行回收)
WeakReference<M> m = new WeakReference<>(new M());
// M对象
System.out.println(m.get());
System.gc();
// null,调用了finalize()方法
System.out.println(m.get());



虚引用
(作用:在NIO中,就运用了虚引用管理堆外内存。)
(特点:无法通过虚引用来获取对一个对象的真实引用。)
(要求:必须和一个队列结合使用,除了写类库的人,一般没人用)
>   “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

>虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。你声明虚引用的时候是要传入一个queue的。当你的虚引用所引用的对象已经执行完finalize函数的时候,就会把对象加到queue里面。你可以通过判断queue里面是不是有对象来判断你的对象是不是要被回收了【这是重点,让你知道你的对象什么时候会被回收。因为对普通的对象,gc要回收它的,你是知道它什么时候会被回收】。
// 运行下面代码的时候需要指定命令行参数(堆最大值20M):-Xmx20M
// 虚引用必须与ReferenceQueue一起使用,当GC准备回收一个对象,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的ReferenceQueue中。
ReferenceQueue<M> queue = new ReferenceQueue<>();
List<byte[]> bytes = new ArrayList<>();
// 虚引用
PhantomReference<M> reference = new PhantomReference<>(new M(),queue);
new Thread(() -> {
    for (int i = 0; i < 100; i++) {
      bytes.add(new byte);
    }
}).start();

new Thread(() -> {
    while (true) {
      Reference<? extends M> poll = queue.poll();
      if (poll != null) {
            System.out.println("虚引用被回收了:" + poll);
      }
    }
}).start();
try {
    System.in.read();
} catch (IOException e) {
    e.printStackTrace();
}

简单分析一下上述代码:
> 第一个线程往集合里面塞数据,随着数据越来越多,肯定会发生GC。
第二个线程死循环,从queue里面拿数据,如果拿出来的数据不是null,就打印出来。
从运行结果可以看到:当发生GC,虚引用就会被回收,并且会把回收的通知放到ReferenceQueue中。

jacker 发表于 2021-1-11 10:34

感谢分享。

左手凯 发表于 2021-1-11 11:09

学到了 感谢分享

回忆童年快乐 发表于 2021-1-11 11:23

多谢分享,都快忘干净了
页: [1]
查看完整版本: java中的引用类型