强引用、软引用、弱引用、虚引用的概念、区别、应用

一、四大引用级别的概念

  • 强引用:就是正常的引用,类似于下面:
    • Object object = new Object();,object就是一个强引用,gc是不会清理一个强引用引用的对象的,即使面临内存溢出的情况。
  • 软引用:SoftReference,GC会在内存不足的时候清理引用的对象:
    • SoftReference reference = new SoftReference(object); object = null;
  • 弱引用:GC线程会直接清理弱引用对象,不管内存是否够用:
    • WeakReference reference = new WeakReference(object); object = null;
  • 虚引用:和弱引用一样,会直接被GC清理,而且通过虚引用的get方法不会得到对象的引用,形同虚设,这里弱引用是可以的:
    • PhantomReference refernce = new PhantomReference(object); object = null;

二、四大引用级别之间的区别

强引用和软引用

  • 这个比较简单,软引用只有在内存不足的时候才会被清理,而强引用什么时候都不会被清理(程序正常运行的情况下),即使是内存不足,利用这一个特性,可以做一些缓存的工作,下面的应用会讲到。

软引用和弱引用

  • 弱引用不会影响GC的清理,也就是说当GC检测到一个对象存在弱引用也会直接标记为可清理对象,而软引用只有在内存告罄的时候才会被清理

弱引用和虚引用

  • 说这个之前要说一下ReferenceQueue的概念,ReferenceQueue是一个队列,初始化Reference的时候可以作为构造函数的参数传进去,这样在该Reference的referent域(Reference用来保存引用对象的属性)指向的引用对象发生了可达性的变化时会将该Reference加入关联的队列中,这个具体的变化根据Reference的不同而不同。官方APi对ReferenceQueue的介绍:
    • Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected.
    • 简单翻译就是:当reference object所引用的对象发生了适当的可达性变化时,GC向该队列追加该引用对象(reference object)。
  • 弱引用和虚引用的区别就在于被加入队列的条件不同,这里主要侧重于考虑对象所属的类重写了finalize方法,将对象的状态归纳为三种:finalizable, finalized、reclaimed,分别代表:未执行finalize函数、已经执行finalize函数,已经回收。如果没有重写finalize函数的话下面再考虑。
  • 虚引用必须和一个ReferenceQueue联合使用,当GC准备回收一个对象的时候,如果发现该对象还有一个虚引用,就会将这个虚引用加入到与之关联的队列.
  • 弱引用:当GC第一次试图回收该引用指向的对象时会执行该对象的finalize方法,然后将该引用加入队列中,但是该引用指向的对象是可以在finlize函数中“复活”的,所以即使通过Reference的get方法得到的是null,而且reference被加入到了ReferenceQueue,这个对象仍然是存活的,这种现象是有点违背对象正常生命周期的,下面以代码示例:
package com;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class Main {
    static Weak weak;
    static class Weak {
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("finalize() is called");
            weak = this;//复活对象
            System.out.println("after finalize ,object is alive again");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        weak = new Weak();
        ReferenceQueue queue = new ReferenceQueue<>();
        WeakReference<Weak> weakReference = new WeakReference<Weak>(weak, queue);
//        PhantomReference<Weak> weakReference = new PhantomReference<>(weak,queue);
        if (weakReference.get() == null) {
            System.out.println("before gc : reference is not available");
        } else {
            System.out.println("before gc : reference is available");
        }
        weak = null;
        System.gc();//执行GC线程
        Thread.sleep(3000);
        if (weakReference.get() == null) {
            System.out.println("after gc : reference is not available");
        } else {
            System.out.println("after gc : reference is available");
        }
        if (queue.poll() == null) {
            System.out.println("after gc : reference is not in queue");
        } else {
            System.out.println("after gc : reference is in queue");
        }
        weak=null;
        System.gc();//再次执行GC
        Thread.sleep(3000);
        if (queue.poll() == null) {
            System.out.println("gc agaain : reference is not in queue");
        } else {
            System.out.println("gc agaain : reference is in queue");
        }
  }    
}
  • output:
    before gc : reference is available
    finalize() is called
    after finalize ,object is alive again
    after gc : reference is not available
    after gc : reference is in queue
    gc agaain : reference is not in queue
  • 可以看到,对象在复活之后,reference不可用,而且reference已经被加入到队列中。
  • 虚引用:虚引用只有在对象处于reclaimed状态时才会将相关reference加入到队列,可以根据这个特性检测对象准确的生命周期,比如可以知道对象精确的销毁时间。
  • 和上面相同的代码,输出如下:
    before gc : reference is not available
    finalize() is called
    after finalize ,object is alive again
    after gc : reference is not available
    after gc : reference is not in queue
    gc again : reference is in queue
  • 我们可以根据输出看到明显的区别:
    • 弱引用可以得到对象的引用,而虚引用不可以
    • 弱引用在第一次被GC清理的时候会调用finalize方法,然后将reference加入到队列,即使这时候的对象是存货状态,而虚引用在第二次GC执行,对象处于reclaimed状态时才会将reference加入到队列。
  • 顺便提一下软引用,在这个方面,软引用和弱引用是一样的,也就是说即使加入到了队列中,也不能确定对象是否销毁。

三、四大引用的应用

软引用

  • 缓存敏感数据
    • 适用情景:一个web应用,查询联系人数据,如果在浏览的过程中点击了返回上一条查询结果,正常处理就是再重新查询一次,然而这样是很浪费时间的,所以如果能将上一次的查询结果缓存下来就会极大的提升性能,所以可以将查询结果保存为软引用,这样在内存充足的情况下GC都不会释放掉这个引用指向的对象,下次需要结果的时候可以先判断一下这个对象是否被释放,如果没有就可以直接利用不用再次查询。
    • 当软引用所指向的对象被回收的时候,通过get方法返回的是null,如果在构造软引用的时候传入了一个ReferenceQueue,就是将这个reference加入到队列,然后我们可以清理这些无用的reference。实际上我们最好这样做,否则会造成大量无用reference导致的内存泄漏。
  • 下面举一个缓存的例子:
package com;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
public class Main {
}
class People{
    String id;
    String name;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
class PeopleCache {
    private static PeopleCache singlonCache;
    private HashMap<String, PeoReference> refChache;//用来存储缓存的数据
    private ReferenceQueue queue;
    //自定义引用类,增加属性_key,方便在缓存数据中查找
    static class PeoReference extends SoftReference {
        private String _key;
        public PeoReference(People referent, ReferenceQueue q) {
            super(referent, q);
            _key = referent.getId();
        }
    }
    private PeopleCache(){
        this.queue=new ReferenceQueue();
        this.refChache=new HashMap<>();
    }
    public static PeopleCache getInstance(){
        if(singlonCache==null){
            singlonCache=new PeopleCache();
        }
        return singlonCache;
    }
    public void cachePeople(People people){
        cleanCache();//清除已经标记为垃圾的引用
        PeoReference reference = new PeoReference(people, queue);
        refChache.put(people.getId(), reference);//将对象的软引用保存到缓存中
    }
    public void cleanCache(){
        PeoReference reference = null;
        while ((reference = (PeoReference)queue.poll())!=null){
            refChache.remove(reference._key);
        }
    }
    public People getCachedPeople(String key){
        People people = null;
        if (refChache.containsKey(key)){
            people= (People) refChache.get(key).get();
            System.out.println("get object from cache");
        }else{
            people = new People();
            System.out.println("get object from database or web server");
        }
        return people;
    }
}
  • 当需要查询数据的时候先获取一个PeopleCache实例,然后使用getCachedPeople方法试图去获取指定ID的数据,如果缓存中有的话就从缓存中获取,没有的话就正常查询获取,并且将查询结果缓存到HashMap。

弱引用

  • WeakHashMap
    • 具体使用和HashMap一样,但是它的键存放的是对象的弱引用,如果该弱引用指向的对象被垃圾回收了,WeakHashMap就会删除对应的数据(键值对)。
    • 用来缓存那些非必需存在的数据。

虚引用

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

推荐阅读更多精彩内容