四、强引用、软引用、弱引用、幻象引用有什么区别

一、概念

  1. 强引用就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还"活着",垃圾收集器不会处理这种对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应引用赋值为null,就是可以被垃圾收集的了,当然具体回收时机还需要看垃圾收集策略。我们使用的大部分引用都是强引用,这是使用最普遍的引用。如:
String strongReference = new String("refenrence");

有一点需要注意的是像下面这种代码中list集合里的数据并不会进行释放,即使内存不足时也不会进行释放:

String strongReference = new String("refenrence");
List<String> list = new ArrayList<>();
list.add(strongReference);
list = null;
System.out.println(list);
System.out.println(strongReference);

这是因为ArrayList是使用数组来实现的,在ArrayList中定义了一个elementData[],如果没有指定ArrayList大小这个数组默认是为空的,只有在第一次添加元素时才会默认初始化容量为10,ArrayList类有一个java.util.ArrayList#clear方法用来清空集合中的元素,会将集合中的每个元素执行

elementData[i] = null;

不同于

list = null;

强引用仍然存在,避免在后续调用add()方法添加元素时进行重新的内存分配。使用如clear()方法释放内存的方法对数组中存放的引用类型非常适用,这样就可以及时释放内存。

  1. 软引用是一种相对弱化一些的引用,可以让对象豁免一些垃圾收集,只有当JVM认为内存不足时才会去试图回收软引用指向的对象。JVM会确保在抛出OutOfMemoryError之前,清理软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时就清理掉,这样就保证了使用缓存的同时,不会耗尽内存
String str = "软引用";
SoftReference softReference = new SoftReference(str);
str = null;
System.out.println(softReference.get());
System.gc();
System.runFinalization();
System.out.println(softReference.get());

上面的例子中两次都会正确输出"软引用",因为软引用只会在JVM认为内存不足时才会清理。

  1. 弱引用并不能使对象豁免垃圾收集,仅仅是提供一种访问在弱引用状态下对象的途径。这就可以用来构建一种没有特定约束的关系,比如维护一种非强制性的映射关系,如果试图获取时对象还在,就使用他否则就重新实例化。软引用适合做缓存而弱引用适合存储元数据
String str = new String("弱引用");
WeakReference weakReference = new WeakReference(str);
str = null;
System.out.println(weakReference.get());
System.gc();
System.runFinalization();
System.out.println(weakReference.get());

上面的例子中只会输出一次"弱引用",因为只要JVM运行GC发现有弱引用便会直接进行清理,并不会关注此时JVM内存是否充足。

  1. 幻象引用仅仅提供了一种确保对象被finalize之后做某些事情的机制,比如通常用来做所谓的post-Mortem清理机制,也有人利用幻想引用来监控对象的创建和销毁。
String str = new String("幻象引用");
ReferenceQueue referenceQueue = new ReferenceQueue();
PhantomReference phantomReference = new PhantomReference(str, referenceQueue);
str = null;
System.out.println(phantomReference.get());
System.gc();
System.runFinalization();
System.out.println(referenceQueue.poll() == phantomReference);

幻想引用在任何时候使用get方法都只会返回null,因为幻想引用总是不可达的。需要注意的是幻象引用必须与引用队列结合起来使用,幻想引用会在对象被释放(执行finalize后)时加入到引用队列之中,软引用与弱引用是在对象被垃圾回收之后会被加入到引用队列中。

不同的引用类型,主要体现的是对象不同的可达性状态和对垃圾收集的影响。

二、引用队列
我们在创建各种引用并关联到相应对象中时,可以选择是否需要关联引用队列,JVM会在特定时机将引用放入引用队列中,我们可以从队列中获取引用进行相关后续逻辑。尤其是幻想引用,get方法返回null,如果不指定引用队列基本就没有意义了。

Object counter = new Object();
ReferenceQueue refQueue = new ReferenceQueue<>();
PhantomReference<Object> p = new PhantomReference<>(counter, refQueue);
counter = null;
System.gc();
try {
    // Remove 是一个阻塞方法,可以指定 timeout,或者选择一直阻塞
    Reference<Object> ref = refQueue.remove(1000L);
    if (ref != null) {
        // do something
    }
} catch (InterruptedException e) {
    // Handle it
}

三、诊断JVM引用情况
如果怀疑应用存在引用导致的回收问题,可以通过指定JVM参数来排查诊断:

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintReferenceGC

四、Reachability Fence
按照Java规范如果一个对象没有指向强引用就符合垃圾收集的标准,有些时候对象并没有强引用,但是也许它得部分属性还在被使用,这样就导致诡异的问题,所以我们需要一个方法,在没有强引用的情况下通知JVM对象是在被使用的。

class Resource {
 private static ExternalResource[] externalResourceArray = ...
 int myIndex; Resource(...) {
     myIndex = ...
     externalResourceArray[myIndex] = ...;
     ...
 }
 protected void finalize() {
     externalResourceArray[myIndex] = null;
     ...
 }
 public void action() {
 try {
     // 需要被保护的代码
     int i = myIndex;
     Resource.update(externalResourceArray[i]);
 } finally {
     // 调用 reachbilityFence,明确保障对象 strongly reachable
     Reference.reachabilityFence(this);
 }
 }
 private static void update(ExternalResource ext) {
    ext.status = ...;
 }
} 

方法action的执行依赖于对象的部分属性,所以被保护了起来。否则如果我们在代码中像这样调用

new Resource().action()

可能就会出现问题,如果没有强引用指向我们创建出来的Resource对象,JVM对它进行finalize是完全合法的。这在新的异步编程模式下可能会非常有用,可以保障对象不被意外回收。

五、项目或者系统中实际使用到引用的案例我暂时还没有遇到,等遇到的时候再来补充吧。

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