详解Java的引用类型及使用场景

每种编程语言都有自己操作内存中元素的方式,例如在 C 和 C++ 里是通过指针,而在 Java 中则是通过“引用”。在 JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱,今天这篇文章就简单介绍一下这四种类型,并简单说一下他们的使用场景。

1. 强引用(Strong Reference)

强引用类型,是我们最常讲的一个类型,我们先看一个例子:

package cn.bridgeli.demo.reference;
 
/**
 * @author BridgeLi
 * @date 2021/2/26 10:02
 */
public class User {
 
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize");
    }
 
}
 
package cn.bridgeli.demo.reference;
 
import org.junit.Test;
 
/**
 * @author BridgeLi
 * @date 2021/2/26 10:03
 */
public class StrongReferenceTest {
 
    @Test
    public void testStrongReference() {
        User user = new User();
        user = null;
        System.gc();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我们都知道当一个实例对象具有强引用时,垃圾回收器不会回收该对象,当内存不足时,宁愿 OOM,也就是抛出 OutOfMemeryError 异常也不会回收强引用的对象,因为 JVM 认为强引用的对象是用户正在使用的对象,它无法分辨出到底该回收哪个,强行回收有可能导致系统严重错误。但是当对象被赋值为 null 之后,会被回收,并且会执行对象的 finalize 函数,此时我们可以通过该函数拯救自己,但是有两点需要注意一个是只能拯救一次,当再次被垃圾回收的时候就不能拯救了,另一个就是有事没事千万不要重写次函数,本例只是为了说明问题重写了此函数,如果在工作中误重写了此函数,可能会导致垃圾不能回收,最终 OOM,另外有熟悉 GC 的同学没?猜一下我为什么要 sleep 一下?

2. 软引用(Soft Reference)

在我刚学 Java 的时候,并不知道怎么使用软引用,那时候只知道强引用,其实是通过 java.lang.ref.SoftReference 类来使用软引用的,为了说明软引用,我们先看一个例子:

package cn.bridgeli.demo.reference;
 
import org.junit.Test;
 
import java.lang.ref.SoftReference;
 
/**
 * @author BridgeLi
 * @date 2021/2/26 10:21
 */
public class SoftReferenceTest {
 
    @Test
    public void testSoftReference() {
        SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024 * 10]);
        System.out.println(softReference.get());
 
        System.gc();
 
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        System.out.println(softReference.get());
 
        byte[] bytes = new byte[1024 * 1024 * 12];
 
        System.out.println(softReference.get());
    }
}

除了通过 get 方法获取我们的软引用对象之外,运行结果和强引用类型并没有什么区别是吧?结果和我们想的一样,但是别着急,加一个启动参数再试试:

-Xms20m -Xmx20m

我们都知道,这两个参数是控制 JVM 启动的时候堆的最大值和最小值的,这里面我们设置的最大值和最小值都是 20M,按照强引用的逻辑,我们一共申请了 22M 的空间,应该 OOM 才对,事实证明并没有,通过打印语句证明,我们的软引用被回收了,所以软引用的特点是:在内存足够的时候,软引用对象不会被垃圾回收器回收,只有在内存不足时,垃圾回收器则会回收软引用对象,当然回收了软引用对象之后仍然没有足够的内存,这时同样会抛出内存溢出异常。

看了软引用的特点,我们很容易想到软引用的使用场景:缓存。记得刚工作的时候,有个同事给我说,他做 Android,有一个加载图片的应用,特麻烦,会 OOM,其实使用软引用应该很轻松的能解决这个问题。

3. 弱引用(Weak Reference)

弱引用是通过 java.lang.ref.WeakReference 类来实现的,同样我们也先看一个例子:

package cn.bridgeli.demo.reference;
 
import org.junit.Test;
 
import java.lang.ref.WeakReference;
 
/**
 * @author BridgeLi
 * @date 2021/2/26 10:30
 */
public class WeakReferenceTest {
 
    @Test
    public void testWeakReference() {
        WeakReference<User> weakReference = new WeakReference<>(new User());
        System.out.println(weakReference.get());
 
        System.gc();
 
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        System.out.println(weakReference.get());
    }
}

通过例子我们可以看到,弱引用是一种比软引用更弱的引用类型:在系统 GC 时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收。看到这里可能会有同学有疑问,GC 什么时候启动,除了我们显示调用外,我们并不能控制(其实就算我们显示调用,GC 也可能不会立即执行),而且 GC 之后,弱引用立即被回收,引用不到了,那么这个类型有什么用呢?其实这个类型还真有大用,我们鼎鼎大名的 ThreadLocal 类就是借助于这个类实现的,所以当你使用 ThreadLocal 的时候,就已经在使用弱类型了,我之前曾经写过关于 ThreadLocal 的文章,但是当时理解不是很准确,不过说明的例子是没有问题的,所以还有一定的参考价值,后面看看啥时候有机会重写一篇关于 ThreadLocal 的文章,详细说说这个类。

另外除了 ThreadLocal 类外还有一个类值得说一下,那就是 java.util.WeakHashMap 类,见名知意,我们就可以猜到这个类的特点。同样通过一个例子说明一下:

package cn.bridgeli.demo.reference;
 
import org.junit.Test;
 
import java.util.Map;
import java.util.WeakHashMap;
 
/**
 * @author BridgeLi
 * @date 2021/2/26 10:38
 */
public class WeakHashMapTest {
 
    @Test
    public void testWeakHashMap() {
        Map map = new WeakHashMap<String, Object>();
        for (int i = 0; i < 10000; i++) {
            map.put("key" + i, new byte[i]);
        }
 
//        Map map = new HashMap<String, Object>();
//        for (int i = 0; i < 10000; i++) {
//            map.put("key" + i, new byte[i]);
//        }
    }
}

记得启动的时候设置一下,设置一下启动的时候堆的大小,不要设置太大,可以看出区别。

4. 虚引用(Phantom Reference)

通过前面的例子,我们可以看到引用强度是越来越弱的,所以虚引用是最弱的一种引用类型,到底有多弱呢,我们同样通过一个例子来看,需要说明的是,虚引用是通过 java.lang.ref.PhantomReference 类实现的。

package cn.bridgeli.demo.reference;
 
import org.junit.Test;
 
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.List;
 
/**
 * @author BridgeLi
 * @date 2021/2/26 11:05
 */
public class PhantomReferenceTest {
 
    ReferenceQueue referenceQueue = new ReferenceQueue();
    List<Object> list = new ArrayList<>();
 
    @Test
    public void testPhantomReference() {
        PhantomReference<Object> phantomReference = new PhantomReference<>(new Object(), referenceQueue);
        System.out.println(phantomReference.get());
 
        new Thread(() -> {
            while (true) {
                Reference reference = referenceQueue.poll();
                if (null != reference) {
                    System.out.println("============ " + reference.hashCode() + " ============");
                }
            }
        }).start();
 
        new Thread(() -> {
            while (true) {
                list.add(new byte[1024 * 1024 * 10]);
            }
        }).start();
 
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我们看到了是什么?虽然软引用和弱引用也很弱,但是我们还是可以通过 get 方法获取到我们的引用对象,但是虚引用却不行,点进去看一下源码,我们可以看到虚引用的 get 方法,直接返回 null,也就是我们直接拿不到虚引用对象,那么这个类型又有什么使用场景呢?其实这个类型就不是给我们普通程序员使用的,在 io、堆外内存中有使用,所以对于我们普通程序员来说,了解到存在这个类型,另外通过上面的例子,我们还可以看到:当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,销毁这个对象,将这个虚引用加入引用队列。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。那么我们就可以在程序中发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取一些必要的行动。

以上就是详解Java的引用类型及使用场景的详细内容,更多关于Java 引用类型及使用场景的资料请关注我们其它相关文章!

(0)

相关推荐

  • JAVA中值类型和引用类型的区别

     1. Java中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值, 那么调用这个方法是传入的变量的值也将改变.值类型表示复制一个当前变量传给方法, 当你在这个方法中改变这个变量的值时,最初生命的变量的值不会变.通俗说法: 值类型就是现金,要用直接用:引用类型是存折,要用还得先去银行取现.----(摘自网上) [值类型] 也就是基本数据类型 基本数据类型常被称为四类八种 四类:  1,整型 2,浮点型 3

  • Java/Android引用类型及其使用全面分析

    Java/Android中有四种引用类型,分别是: Strong reference - 强引用 Soft Reference - 软引用 Weak Reference - 弱引用 Phantom Reference - 虚引用 不同的引用类型有着不同的特性,同时也对应着不同的使用场景. 1.Strong reference - 强引用 实际编码中最常见的一种引用类型.常见形式如:A a = new A();等.强引用本身存储在栈内存中,其存储指向对内存中对象的地址.一般情况下,当对内存中的对象

  • 详解Java引用类型的参数也是值传递

    简述 调用方法的时候,有需要传参数的情况.在Java中,参数的类型有基本类型和引用类型两种. 一开始听到一个说法,Java没有引用传递,但是一直没有太多的思考在上面,直到前不久玩数组的时候,突然间发现把数组引用变量作为参数传递到一个方法当中进行操作之后,再去访问原数组,尽然改变了.于是乎,就想到了之前在C++里面学过的引用传递,突然有一种错愕的感觉,就查了一些资料,探究当Java引用类型变量作为参数传递给方法的时候,到底是值传递还是引用传递. 结论:如果将Java引用类型变量作为参数传递给方法,

  • java的引用类型的详细介绍

    •强引用(FinalReference),在java中,有点像C++的指针,通过引用,可以对堆中的对象进行操作.强引用具备以下特点: 1.强引用可以直接访问目标对象:2.强引用所指向的对象在任务时候都不会被系统回收:3.强引用可能导致内存泄露.•软引用(SoftReference),软引用对象,在响应内存需要时,由垃圾回收器决定是否清除此对象.一个持有软件引用的对象,不会被JVM很快回收,只要有足够的内存,软件引用便可能在内存中存活相当长的时间,软引用对象最常用于实现内存敏感的缓存:•弱引用(W

  • Java中四种引用类型详细介绍

    Java 四种引用类型 对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期.这4种级别由高到低依次为:强引用.软引用.弱引用和虚引用. ⑴强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足,

  • java中的引用类型之强软弱虚详解

    前言 java中的引用类型共4种:强软弱虚,具体每种类型的特点和应用场景.记录下.本文是看了马士兵老师的视频后记录整理的.加深印象. 基本概念 1. 强引用 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题. 显式地设置M对象为null,或让其超出对象的生命周期范围,则gc认为该对象不存在引用,这时就可以回收这个对象 示例代

  • 浅谈Java 中的引用类型

    Java 中的引用类型:强引用.软引用.弱引用和虚引用 强引用 如 Object object = new Object(),那 object 就是一个强引用,如果一个对象具有强引用,垃圾回收器就永远不会回收它. 软引用 软引用用来描述一些还有用但非必需的对象.在内存即将发生内存溢出之前,会把这些对象列进回收范围之中进行二次垃圾回收.如果这次回收还没有足够内存,才会发生内存溢出现象. 另:软引用可用来实现内存敏感的高速缓存. 弱引用 用来描述非必需的对象.被弱引用关联的对象只能存活到下一次垃圾收

  • 全面解析Java中的引用类型

    如果一个内存中的对象没有任何引用的话,就说明这个对象已经不再被使用了,从而可以成为被垃圾回收的候选.不过由于垃圾回收器的运行时间不确定,可被垃圾回收的对象的实际被回收时间是不确定的.对于一个对象来说,只要有引用的存在,它就会一直存在于内存中.如果这样的对象越来越多,超出了JVM中的内存总数,JVM就会抛出OutOfMemory错误.虽然垃圾回收的具体运行是由JVM来控制的,但是开发人员仍然可以在一定程度上与垃圾回收器进行交互,其目的在于更好的帮助垃圾回收器管理好应用的内存.这种交互方式就是使用J

  • Java中值类型和引用类型的比较与问题解决

    一.问题描述 前几天因为一个需求出现了Bug.说高级点也挺高级,说白点也很简单.其实也就是一个很简单的Java基础入门时候的值类型和引用类型的区别.只是开发的时候由于自己的问题,导致小问题的出现.还好突然想起来以前看过一篇对于该问题讲解的博客,才能快速定位问题的位置.防止下次再犯,顺便也就把这个当做笔记记录下来,放入自己的Bug集中. 二.值类型和引用类型的比较 这个大家应该都是没问题的,很简单.值类型比较是比较值,引用类型是比较地址.对于正常的操作来说,比较值类型我们可以直接使用 == ,引用

  • 详解Java的引用类型及使用场景

    每种编程语言都有自己操作内存中元素的方式,例如在 C 和 C++ 里是通过指针,而在 Java 中则是通过"引用".在 JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference).软引用(Soft Reference).弱引用(Weak Reference).虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱,今天这篇文章就简单介绍一下这四种类型,并简单说一下他们的使用场景. 1. 强引用(Strong

  • 详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    前言 CountDownLatch和CyclicBarrier两个同为java并发编程的重要工具类,它们在诸多多线程并发或并行场景中得到了广泛的应用.但两者就其内部实现和使用场景而言是各有所侧重的. 内部实现差异 前者更多依赖经典的AQS机制和CAS机制来控制器内部状态的更迭和计数器本身的变化,而后者更多依靠可重入Lock等机制来控制其内部并发安全性和一致性. public class { //Synchronization control For CountDownLatch. //Uses

  • 详解Java 反射和反射的应用场景

    反射机制介绍 JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制. 获取 Class 对象的两种方式 如果我们动态获取到这些信息,我们需要依靠 Class 对象.Class 类对象将一个类的方法.变量等信息告诉运行的程序.Java 提供了两种方式获取 Class 对象: 1.知道具体类的情况下可以使用: Class alunbarCla

  • 一文详解Java etcd的应用场景及编码实战

    目录 一.白话etcd与zookeeper 二.etcd的4个核心机制 三.Leader选举与客户端交互 四.etcd的应用场景 4.1. kubernetes大脑 4.2. 服务注册与发现 4.3. 健康检查与状态变更通知 4.4.分布式锁 4.5.实现消息队列(纯扯淡) 五.etcd安装 六.jetcd的编码实现配置管理 本文首先用大白话给大家介绍一下etcd是什么?这部分内容网上已经有很多了. etcd有哪些应用场景?这些应用场景的核心原理是什么? 最后不能光动嘴不动手.先搭建一个etcd

  • 详解Java中的不可变对象

    不可变对象想必大部分朋友都不陌生,大家在平时写代码的过程中100%会使用到不可变对象,比如最常见的String对象.包装器对象等,那么到底为何Java语言要这么设计,真正意图和考虑点是什么?可能一些朋友没有细想过这些问题,今天我们就来聊聊跟不可变对象有关的话题. 一.什么是不可变对象 下面是<Effective Java>这本书对于不可变对象的定义: 不可变对象(Immutable Object):对象一旦被创建后,对象所有的状态及属性在其生命周期内不会发生任何变化. 从不可变对象的定义来看,

  • 详解JAVA 虚引用

    定义 虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个.一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获得一个对象实例. 说明 虚引用,正如其名,对一个对象而言,这个引用形同虚设,有和没有一样. 如果一个对象与GC Roots之间仅存在虚引用,则称这个对象为虚可达(phantom reachable)对象. 当试图通过虚引用的get()方法取得强引用时,总是会返回null,并且,虚引用必须和引用队列一

  • 详解JAVA 弱引用

    定义 弱引用是使用WeakReference创建的引用,弱引用也是用来描述非必需对象的,它是比软引用更弱的引用类型.在发生GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收. 说明 弱引用,从名字来看就很弱嘛,这种引用指向的对象,一旦在GC时被扫描到,就逃脱不了被回收的命运. 但是,弱引用指向的对象也并不一定就马上会被回收,如果弱引用对象较大,直接进到了老年代,那么就可以苟且偷生到Full GC触发前,所以弱引用对象也可能存在较长的一段时间.一旦一个弱引用对象被垃圾回收器回收,便

  • 详解Java 虚拟机垃圾收集机制

    1 垃圾收集发生的区域 之前我们介绍过 Java 内存运行时区域的各个部分,其中程序计数器.虚拟机栈.本地方法栈三个区域随线程共存亡.栈中的每一个栈帧分配多少内存基本上在类结构确定下来时就已知,因此这几个区域的内存分配和回收都具有确定性,不需要考虑如何回收的问题,当方法结束或线程结束,内存自然也跟着回收了 而 Java 堆和方法区这两个区域则有显著的不确定性,只有在程序运行时我们才能知道程序究竟创建了哪些对象,创建了多少对象,所以这部分内存的分配和回收是动态的,垃圾收集器所关注的正是这部分内存该

  • 详解Java中的反射机制和动态代理

    一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制.通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以,这是一种动态获取类的信息以及动态调用对象方法的能力. 想要使用反射机制,就必须要先获取到该类

  • 两万字详解Java Sring String的常见操作以及StringBuffer StringBuilder的区别

    目录 前言 1. 定义字符串 2.字符串比较相等 equals 使用注意事项 3. 字符串常量池( 多图解析 ) 第一个列子解析: 第二个列子解析: 第三个列子解析: 第四个列子解析: 第五个列子解析: 第六个列子解析: 第七个列子解析: 第八个列子解析: 面试题:请解释String类中两种对象实例化的区别 4. 理解字符串不可变 5.字符, 字节与字符串 字节与字符串 小结 6. 字符串常见操作 6.1 字符串比较 6.2 字符串查找 6.3 字符串替换 6.4 字符串拆分 6.5 字符串截取

随机推荐