Java四种引用类型

Java四种引用类型

一 什么是引用

Object obj = new Object();

img

如图所示 , Java中变量存放在栈 , 对象存放在堆 , 变量的值实际是指向对象在堆内存中的初始地址 , 所以我们也称这块堆内存存在着引用 .

接下来看这段代码:

new Object();

请问, 该对象存在引用吗?答案是否. 因为在栈中(或其他地方)不存在该对象的地址 , 则该对象不存在被引用

image-20210831100416242

再看一段代码:

List list = new ArrayList();
list.add(new Object());

请问: new 出来的Object对象存在引用吗? 答案是存在 , 因为该对象被list所引用

image-20210831100517407

在对引用有一个简单大概的了解之后 , 接下来我们来看看Java具体存在着哪些引用类型.

二 Java的四种引用类型

1)强引用

Object obj = new Object();

Java默认就是强引用 . 强引用的作用是: GC时JVM永远不会回收强引用对象 , 即使内存不足(会直接抛出OOM Error: Java heap space) ; 如果要回收强引用对象 , 则必须先断开引用,即设置obj为null

obj = null;

然后再等待下一次GC时回收该对象 .

For example:

public class Test2 {
    public static void main(String[] args) {
        List list = new ArrayList();
        for (int i = 1; i <= 10; i++) {
            System.out.print("当前是第"+i+"次添加:");
            byte[] bytes = new byte[1024 * 1024];// 往list中添加一个1m的对象,添加十次
            list.add(bytes);
            System.out.println("添加成功");
        }
    }
}

我们设置jvm启动参数(设置堆启动内存以及最大内存都为5m)

-Xms5M -Xmx5M

查看执行结果

当前是第1次添加:添加成功
当前是第2次添加:添加成功
当前是第3次添加:添加成功
当前是第4次添加:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at com.ibm.cn.se.reference.Test2.main(Test2.java:17)

分析执行结果😦分析之前, 我们得先知道IDEA在启动程序时, 会最起码占用1.5m左右的内存)

for循环执行10次, 每次往list中添加一个1m的对象 , 前3次都添加成功 , 共添加了3m,再加上idea的启动内存一共4.5m左右 ; 当第四次再添加1m时, 装不下了(4.5m+1m>5m), 此时的容量已经超过了我们设置的堆最大内存(5m), 但是jvm并不会回收强引用对象,所以 jvm抛出了错误(OOM ERROR : java heap space)

结论: jvm无论内存足够与否GC时都不会回收强引用对象

2)软引用

软引用需要用到SoftRefercence类, 与强引用的区别是:

  • 内存足够时: 强引用对象和软引用对象都不会被回收
  • 内存不足时: 软引用对象会被回收, 而强引用对象不会
强引用对象软引用对象
内存足够不会回收不会回收
内存不足不会回收会被回收

For example:

public class Test3 {
    public static void main(String[] args) {
        List list = new ArrayList();
        for (int i = 1; i <= 10; i++) {
            System.out.print("当前是第"+i+"次添加:");
            byte[] bytes = new byte[1024 * 1024];// 往list中添加一个1m的对象,添加十次
            SoftReference softReference = new SoftReference<>(bytes);
            list.add(softReference);
            System.out.println("添加成功");
            // 打印list当前存在的对象
            for (int j = 0; j < list.size(); j++) {
                System.out.println("    列表下标:"+j+"所对应的值:"+((SoftReference)list.get(j)).get());
            }
        }

    }
}

同样的设置jvm启动参数

-Xms5M -Xmx5M

执行程序 , 查看执行结果

当前是第1次添加:添加成功
    列表下标:0所对应的值:[B@33c7353a
当前是第2次添加:添加成功
    列表下标:0所对应的值:[B@33c7353a
    列表下标:1所对应的值:[B@681a9515
当前是第3次添加:添加成功
    列表下标:0所对应的值:[B@33c7353a
    列表下标:1所对应的值:[B@681a9515
    列表下标:2所对应的值:[B@3af49f1c
当前是第4次添加:添加成功
    列表下标:0所对应的值:null
    列表下标:1所对应的值:null
    列表下标:2所对应的值:null
    列表下标:3所对应的值:[B@19469ea2
当前是第5次添加:添加成功
    列表下标:0所对应的值:null
    列表下标:1所对应的值:null
    列表下标:2所对应的值:null
    列表下标:3所对应的值:[B@19469ea2
    列表下标:4所对应的值:[B@13221655
当前是第6次添加:添加成功
    列表下标:0所对应的值:null
    列表下标:1所对应的值:null
    列表下标:2所对应的值:null
    列表下标:3所对应的值:[B@19469ea2
    列表下标:4所对应的值:[B@13221655
    列表下标:5所对应的值:[B@2f2c9b19
当前是第7次添加:添加成功
    列表下标:0所对应的值:null
    列表下标:1所对应的值:null
    列表下标:2所对应的值:null
    列表下标:3所对应的值:null
    列表下标:4所对应的值:null
    列表下标:5所对应的值:null
    列表下标:6所对应的值:[B@31befd9f
当前是第8次添加:添加成功
    列表下标:0所对应的值:null
    列表下标:1所对应的值:null
    列表下标:2所对应的值:null
    列表下标:3所对应的值:null
    列表下标:4所对应的值:null
    列表下标:5所对应的值:null
    列表下标:6所对应的值:[B@31befd9f
    列表下标:7所对应的值:[B@1c20c684
当前是第9次添加:添加成功
    列表下标:0所对应的值:null
    列表下标:1所对应的值:null
    列表下标:2所对应的值:null
    列表下标:3所对应的值:null
    列表下标:4所对应的值:null
    列表下标:5所对应的值:null
    列表下标:6所对应的值:[B@31befd9f
    列表下标:7所对应的值:[B@1c20c684
    列表下标:8所对应的值:[B@1fb3ebeb
当前是第10次添加:添加成功
    列表下标:0所对应的值:null
    列表下标:1所对应的值:null
    列表下标:2所对应的值:null
    列表下标:3所对应的值:null
    列表下标:4所对应的值:null
    列表下标:5所对应的值:null
    列表下标:6所对应的值:null
    列表下标:7所对应的值:null
    列表下标:8所对应的值:null
    列表下标:9所对应的值:[B@548c4f57

Process finished with exit code 0

分析结果(IDEA启动内存占用大小约1.5m左右):

当前三次添加后,堆内存都能装的下; 当第四次添加时, 剩余堆空间为0.5m(5m-4.5m) , 不足以放下一个新的对象 , 所以jvm进行了一次GC , 回收掉软引用对象(list下标为0,1,2的对象) , 所以下标0,1,2所指向的值为null ;

当前是第4次添加:添加成功
    列表下标:0所对应的值:null
    列表下标:1所对应的值:null
    列表下标:2所对应的值:null
    列表下标:3所对应的值:[B@19469ea2

GC完毕之后, 堆内存又有空间了 , 此时往list添加一个对象成功(下标为3). 后续执行结果以此类推.

结论:

当堆空间内存足够时 , jvm不会回收掉软引用对象; 但是一旦内存不足时, 则会进行GC, 回收掉软引用对象

3)弱引用

软引用需要用到WeakReference 类, 与强引用, 软引用的区别是:

  • 内存足够时: 强引用对象和软引用对象都不会被回收 , 但弱引用对象会被回收
  • 内存不足时: 软引用对象和弱引用对象会被回收, 而强引用不会

简而言之: 只要进行GC, 弱引用对象就一定会被回收

强引用对象软引用对象弱引用对象
内存足够不会回收不会回收会被回收
内存不足不会回收会被回收会被回收

For example:

public class Test4 {
    public static void main(String[] args) {
        List list = new ArrayList();
        for (int i = 1; i <= 3; i++) {
            System.out.print("当前是第"+i+"次添加:");
            byte[] bytes = new byte[1024 * 1024];// 往list中添加一个1m的对象,添加三次
            // 第一次添加强引用对象
            if(i == 1){
                list.add(bytes);
            }else if(i == 2){
                // 第二次添加软引用对象
                SoftReference softReference = new SoftReference<>(bytes);
                list.add(softReference);
            }else if(i == 3){
                // 第三次添加弱引用对象
                WeakReference weakReference = new WeakReference<>(bytes);
                list.add(weakReference);
            }
            System.out.println("添加成功");
        }
        System.out.println("添加完毕,查看列表:");
        printList(list);
        System.gc();
        System.out.println("GC一次后再次查看列表");
        printList(list);
    }

    private static void printList(List list ){
        // 打印list当前存在的对象
        System.out.println("    列表下标0所对应的值:"+list.get(0));
        System.out.println("    列表下标1所对应的值:"+((SoftReference)list.get(1)).get());
        System.out.println("    列表下标2所对应的值:"+((WeakReference)list.get(2)).get());
    }
}

同样的设置jvm启动参数

-Xms5M -Xmx5M

执行程序 , 查看执行结果

image-20210831103052730

分析结果:

我们给list循环三次添加1m的对象 :

  • 第一次添加了强引用对象(java默认就是强引用)
  • 第二次添加了软引用对象
  • 第三次添加了弱引用对象

添加了三个对象之后, 当前堆内存空间大小=1.5m+3m<5m , 所以并未触发GC , 此时我们查看列表发现三个下标都有数据;

当我们手动调用 System.gc()后,发现下标为2的弱引用对象被回收了, 而下标为0的强引用对象和下标为1的软引用对象并未被回收

结论:

jvm无论内存足够与否GC时都一定会回收弱引用对象

4)虚引用

虚引用(phantomReference)是一种特殊的引用类型 , 这种类型在实例化时与其他类型不同 , 需要传入ReferenceQueue参数,

ReferenceQueue QUEUE = new ReferenceQueue<>();
PhantomReference reference = new PhantomReference<>(new Object() , QUEUE);

且调用get()方法获取到的永远是一个null对象

System.out.println(reference.get());

image-20210901094213922

以下是我找资料时截取的一段话,供大伙参考

虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

原文链接:https://blog.csdn.net/qq_39192827/article/details/85611873

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值