你知道Java四种引用吗?以及他们是如何回收的?

Java四种引用

  • 强引用:有GC ROOT直接引用的对象,当没有再被GCROOT引用的时候,可以被垃圾回收
  • 软引用:当被引用的对象只被软引用时,当发生垃圾回收且内存空间匮乏时会删除软引用所引用的对象,可以通过引用队列来释放引用自身
  • 弱引用:当被引用的对象只被弱引用时,当发生垃圾回收时就会被回收弱引用所引用的对象,可以通过引用队列来释放引用自身
  • 虚引用:一般在ByteBuffer开辟直接内存联系,当ByteBuffer本身被垃圾回收之后,但是对于其开辟的内存空间无法被回收(这是因为直接内存不属于JVM所管理的内存,而属于操作系统内存)。因此虚引用会记录该直接内存地址,并且在虚引用的内部会有一个Cleaner对象,当ByteBuffer被垃圾回收之后,虚引用一定会进入引用队列,而该队列会有一个ReferenceHandler线程去调用队列中对象的clean方法去释放直接内存。

引用队列

引用队列 】用于将软引用和弱引用、虚引用的引用者放入该队列,当他们所引用的对象被垃圾回收之后,Reference引用关系就会进入该队列等待被线程回收。其中软引用和弱引用可以不配合引用队列使用,但是虚引用一定需要配合该队列。

实例程序

前提:为了测出效果,这里把堆内存的大小修改为20M,在启动参数中添加-Xmx20m;

- 强引用
public static void main(String[] args) {
    List<byte[]> list = new ArrayList<>();    
    for (int i = 0; i < 4; i++) {
        list.add(new byte[1024 * 1024 * 5]);    
    }
}
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

说明:
这种是强引用的案例,我们日常工作中大多数对象都是被强引用。即GC Roots所能直接或间接的引用到的对象。这部分在GC时不会被回收,因为这些byte[]始终被生命周期更长的list引用着。因此当byte[]太多之后势必造成堆OOM。

- 软引用
public static void soft() {
    List<SoftReference<byte[]>> lists = new ArrayList<>();    
    for (int i = 0; i < 5; i++) {
        lists.add(new SoftReference<>(new byte[1024 * 1024 * 4]));
        System.out.println("==============================");
        for (SoftReference<byte[]> list : lists) {
             System.out.println(list.get()); 
        }
    }
}
运行结果:
==============================
[B@682a0b20
==============================
[B@682a0b20
[B@3d075dc0
==============================
[B@682a0b20
[B@3d075dc0
[B@214c265e
==============================
[B@682a0b20
[B@3d075dc0
[B@214c265e
[B@448139f0
==============================
null
null
null
null
[B@7cca494b

说明:软引用在发生GC时,且堆内存空间不足时,就会释放被软引用所引用的对象。

- 弱引用
public static void weak() {
    List<WeakReference<byte[]>> lists = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        lists.add(new WeakReference<>(new byte[1024*1024*4]));
        System.out.println("========================");
        for (WeakReference<byte[]> list : lists) {
            System.out.println(list.get());
        }
   }
}
运行结果:
========================
[B@682a0b20
========================
[B@682a0b20
[B@3d075dc0
========================
[B@682a0b20
[B@3d075dc0
[B@214c265e
========================
null
null
null
[B@448139f0
========================
null
null
null
[B@448139f0
[B@7cca494b

说明:软引用在GC时,不管内存是否够用都会删除只被软引用所引用的对象。

- 虚引用
public static void fake() throws IOException {
    Runnable runnable = () -> System.out.println("虚引用");
    Object obj = new Object();
    Cleaner.create(obj, runnable);
    System.in.read();
    obj = null;
    System.gc();
    System.in.read();
}
运行结果:
虚引用

说明:如上是一个虚引用的小Demo,也是ByteBuffer开辟直接内存的一个回收缩影。上面的核心类为Cleaner类,该类继承PhantomReference类,所以其本质就是一个虚引用。上述的create方法就是使用虚引用来引用obj对象,当obj对象被垃圾释放时,引用关系会被放入到ReferenceQueue队列中,之后会有一个线程ReferenceHandler线程一直从Queue中取出Reference对象并调用clean方法,而clean方法就会调用create传入的第二个线程参数。
源码如下:

  • 调用Cleaner.create方法时,会构造Cleaner对象,并将传入的Runnable赋值给全局变量thunk:


    1485_0.png
  • clean方法被调用时,会调用thunk线程的run执行用户自定义的释放动作:


    1487_0.png
  • Reference中有一个线程以最低优先级一直在运行:


    1489_0.png
  • 如下是上述图片中的tryHandlePending方法,方法比较长,这里只截取一段,其中c是Cleaner的实例:


    1491_0.png
  • 这里你可能有个疑问,就是那么多的Cleaner对象,他怎么知道调用哪个Cleaner对象的clean方法呢?


    1493_0.png

    看来这一切都是这个pending的变量才是源头,看一下这个变量的定义吧,注释说的还是很清晰的:


    1495_0.png

回顾引用队列

在了解了上述背景之后,你可能想问。在软、弱引用。在被引用的对象在相应的场景被释放之后。那么这一层Reference引用关系是如何被清理的呢?仔细看一下下一段源码:


1497_0.png

可以看到,ReferenceHandler线程会把垃圾回收器带来的Reference对象置空以下一次被垃圾回收(这个discovered就是Reference实例)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容