Java 引用类型

JDK1.2之后,java对引用的概念进行了拓充,将引用分为强引用,软引用,弱引用,虚引用

  1. 强引用: 指的是在代码之中普遍存在的,类似Object obj = new Object() 这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象
  2. 软引用: 用来描述一些还有用,但是并非重要的对象.对于软引用关联着的对象,在系统将要发生内存溢出之前,将会把这些对象列进回收范围之中并进行第二次回收.如果这次回收还是没有足够的内存,才会抛出内存溢出异常.
  3. 弱饮用: 当垃圾收集器工作时,无论是否内存足够,都将回收掉只被若饮用关联的对象
  4. 虚引用: 一个对象是否是有虚引用的存在,完全不会对其生成时间构成影响,也无法通过虚引用来取得一个对象实例.为一个对象设置虚引用关联的唯一目的是希望在其被收集器回收时收到一个系统通知.

强引用

JVM在GC的时候并不会释放强引用的堆实例, 因此当堆内GC后仍然不能获得足够的空间, 就会发生OOM

String str = new String("Hi");

上面的例子中, 在栈中分配的str指向了堆中分配的String实例, 那么str引用就是这个实例的强引用.

保存在数组和集合中以及Map中的引用都都算是强引用.

软引用

JVM在GC时不一定会释放软引用所引用的对象实例, 那什么时候会进行释放呢? 只有当JVM发现堆内存不足时, 才会在GC时将软引用的堆内存释放掉

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

public class TestSoft {

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
        User user = new User();
        SoftReference<User> softReference = new SoftReference<>(user, referenceQueue);
        user = null;

        Thread t = new Thread(() -> {
            while (true) {
                Reference<? extends User> ref = referenceQueue.poll();
                if (ref != null) {
                    System.out.println("Changed : " + ref);
                    break;
                }
            }
        });

        t.setDaemon(true);
        t.start();

        System.out.println("Before GC : " + " " + softReference.get());

        System.gc();
        System.out.println("After GC : " + softReference.get());

        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate : " + softReference.get());
    }
}

class User {
    public String name;
}

我们指定虚拟机参数-Xmx10M -Xms10M -XX:PrintGC, 运行一下这个程序的结果为:

[GC (Allocation Failure)  2048K->836K(9728K), 0.0023890 secs]
Before GC :  testRef.User@404b9385
[GC (System.gc())  1145K->844K(9728K), 0.0013400 secs]
[Full GC (System.gc())  844K->750K(9728K), 0.0085260 secs]
After GC : testRef.User@404b9385
[GC (Allocation Failure)  788K->782K(9728K), 0.0003760 secs]
[GC (Allocation Failure)  782K->782K(9728K), 0.0002590 secs]
[Full GC (Allocation Failure)  782K->750K(9728K), 0.0043290 secs]
[GC (Allocation Failure)  750K->750K(9728K), 0.0004580 secs]
[Full GC (Allocation Failure)  750K->692K(9728K), 0.0079430 secs]
Changed : java.lang.ref.SoftReference@19366529
Alocate : null

我们在构建SoftReference实例对象时, 除了添加一个测试对象外, 还添加里一个ReferenceQueue实例对象, 当对象的可达状态发生改变时, SoftReference就会移动到ReferenceQueue队列里. 从最后的Poll 这个输出里我们可以看到, 已经看不到这个对象了.

弱引用

弱引用是一种比软饮用更加弱的引用, JVM在GC时只要发现弱引用, 都会对其引用的实例进行回收

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

public class TestSoft {

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
        User user = new User();
        WeakReference<User> softReference = new WeakReference<>(user, referenceQueue);
        // 确定没有强引用
        user = null;

        Thread t = new Thread(() -> {
            while (true) {
                Reference<? extends User> ref = referenceQueue.poll();
                if (ref != null) {
                    System.out.println("Changed : " + ref);
                    break;
                }
            }
        });

        t.setDaemon(true);
        t.start();

        System.out.println("Before GC : " + " " + softReference.get());

        System.gc();
        System.out.println("After GC : " + softReference.get());

        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate : " + softReference.get());

    }
}

class User {}

我们指定虚拟机参数-Xmx10M -Xms10M -XX:+PrintGC, 运行一下这个程序的结果为:

[GC (Allocation Failure)  2048K->800K(9728K), 0.0031060 secs]
Before GC :  null
Changed : java.lang.ref.WeakReference@175fdc70[GC (System.gc())  1084K->824K(9728K), 0.0011480 secs]
[Full GC (System.gc())  824K->748K(9728K), 0.0088060 secs]

After GC : null
[GC (Allocation Failure)  807K->812K(9728K), 0.0010100 secs]
[GC (Allocation Failure)  812K->844K(9728K), 0.0004150 secs]
[Full GC (Allocation Failure)  844K->748K(9728K), 0.0090930 secs]
[GC (Allocation Failure)  748K->748K(9728K), 0.0003230 secs]
[Full GC (Allocation Failure)  748K->690K(9728K), 0.0082600 secs]
Alocate : null

如果WeakReference是保存在一个对象实例里面是什么情况呢?

import java.lang.ref.WeakReference;

public class TestSoft {
    public static void main(String[] args) throws InterruptedException {
        WeakReferenceCache weakReferenceCache = new WeakReferenceCache();
        weakReferenceCache.cache = new WeakReference<>(new User());
        System.out.println("Before GC : " + weakReferenceCache.cache.get());
        System.gc();
        System.out.println("After GC : " + weakReferenceCache.cache.get());
        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate GC : " + weakReferenceCache.cache.get());
    }
}

class WeakReferenceCache {
    public WeakReference<User> cache;

}
class User {
}

同样我们运行一下看一下结果

Before GC : User@41629346
After GC : null
Alocate GC : null

确实是, 每次GC都将其回收掉了

我们再实验一下, 如果将其存进一个列表里

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

public class TestSoft {
    public static void main(String[] args) throws InterruptedException {
        WeakReferenceCache weakReferenceCache = new WeakReferenceCache();
        for (int i = 0; i < 10; i++) {
            WeakReference<User> softReference = new WeakReference<>(new User());
            weakReferenceCache.cache.add(softReference);
        }

        System.out.println("Before GC : ");
        weakReferenceCache.cache.forEach(cache -> {
            System.out.println(cache.get());
        });
        System.gc();
        System.out.println("After GC : ");
        weakReferenceCache.cache.forEach(cache -> {
            System.out.println(cache.get());
        });
        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate GC : ");
        weakReferenceCache.cache.forEach(cache -> {
            System.out.println(cache.get());
        });
    }
}

class WeakReferenceCache {
    public List<WeakReference<User>> cache = new ArrayList<>();

}
class User {
}

结果为

Before GC : 
User@6433a2
User@5910e440
User@6267c3bb
User@533ddba
User@246b179d
User@7a07c5b4
User@26a1ab54
User@3d646c37
User@41cf53f9
User@5a10411
After GC : 
null
null
null
null
null
null
null
null
null
null
Alocate GC : 
null
null
null
null
null
null
null
null
null
null

即使是存储在数组里也一样被回收掉了

虚引用

虚引用是所有引用类型中最弱的一个, 一个被虚引用持有的对象跟没有被持有的效果基本上是一样的. 当我们从虚引用中get时, 总会获得一个空, 那既然如此还为什么要设计出一个这样的引用呢? 因为虚引用必须跟一个引用队列, 我们可以将一些资源性的东西放到虚引用中执行和记录.

import java.lang.ref.*;

public class TestSoft {

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
        User user = new User();
        PhantomReference<User> softReference = new PhantomReference<>(user, referenceQueue);
        user = null;

        Thread t = new Thread(() -> {
            while (true) {
                Reference<? extends User> ref = referenceQueue.poll();
                if (ref != null) {
                    System.out.println("Changed : " + System.currentTimeMillis());
                    break;
                }
            }
        });

        t.setDaemon(true);
        t.start();

        System.out.println("Before GC : " + System.currentTimeMillis() + " " + softReference.get());

        System.gc();
        System.out.println("After GC : " + softReference.get());

        byte[] array = new byte[1024 * 920 * 7];
        System.out.println("Alocate : " + softReference.get());

    }
}

class User {}

我们指定虚拟机参数-Xmx30M -Xms30M -XX:+PrintGC, 运行一下这个程序的结果为:

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