首页 > 编程语言 >美团一面:说一说Java中的四种引用类型?

美团一面:说一说Java中的四种引用类型?

时间:2024-03-22 11:24:07浏览次数:45  
标签:说一说 Java 对象 美团 回收 垃圾 引用 内存

引言

在JDK1.2之前Java并没有提供软引用、弱引用和虚引用这些高级的引用类型。而是提供了一种基本的引用类型,称为Reference。并且当时Java中的对象只有两种状态:被引用和未被引用。当一个对象被引用时,它将一直存在于内存中,直到它不再被任何引用指向时,才会被垃圾回收器回收。而被引用也就是强引用。

而在JDK1.2之后对引用的概念进行了扩充,分为了强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference),这4种引用的强度依次减弱。他们的关系如下如:
image.png

强引用

强引用是Java中最常见的引用类型。当你创建一个对象并将其赋值给一个变量时,这个变量会持有该对象的强引用。

Order order = new Order(); // 只要order还指向Order对象,那么Order对象就不会被回收
order = null; // 强引用都被设置为 null 时,不可达,则Order对象被回收

只要存在强引用指向对象,垃圾回收器将永远不会回收该对象,即使内存不足也不会回收。这可能导致内存溢出,因为即使内存不足,JVM也不会回收强引用对象。当强引用都被设置为null时,对象变成不可达状态,垃圾回收器会在适当的时候将其回收。

比如以下示例,我们创建一个2M的数组,但是我们设置JVM参数:-Xms2M -Xmx3M,将JVM的初始内存设为2M,最大可用内存为3M。

public static void main(String[] args) {  
    //定义一个2M的数组  
    byte[] objects = new byte[1024 * 1024 * 2];  
}

此时我们执行方法后,发现报错:

image.png
对于强引用,即使内存不够使用,直接报错OOM,强引用也不会被回收。

对于强引用,就好比生活中,当我们拥有家里的钥匙时,我们可以随时进入你的家,即使我们不需要进入,也能确保我们可以进入。钥匙是我们进入家的强引用。只有当我们不再拥有钥匙时,我们才无法进入家,类似于当没有强引用指向一个对象时,该对象才能被垃圾回收。

软引用

在JDK1.2之后,用java.lang.ref.SoftReference类来表示软引用。软引用允许对象在内存不足时被垃圾回收器回收。如果一个对象只有软引用指向它,当系统内存不足时,垃圾回收器会尝试回收这些对象来释放内存,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。软引用适用于需要缓存大量对象,但又希望在内存不足时释放部分对象以避免内存溢出的情况,用于实现缓存时,当内存紧张时,可以释放部分缓存对象以保证系统的稳定性。

以下示例我们设置JVM参数为:-Xms3M -Xmx5M,然后连续创建了10个大小为1M的字节数组,并赋值给了软引用,然后循环遍历将这些对象打印出来。

private static final List<Object> list = Lists.newArrayList();  
  
public static void main(String[] args) {  
    IntStream.range(0, 10).forEach(i -> {  
        byte[] buff = new byte[1024 * 1024];  
        SoftReference<byte[]> sr = new SoftReference<>(buff);  
        list.add(sr);  
    });  
  
    System.gc(); // 主动通知垃圾回收  
  
    list.forEach(l -> {  
        Object obj = ((SoftReference<?>) l).get();  
        System.out.println("Object: " + obj);  
    });  
}

然后我们执行代码之后:

image.png
对于打印结果中,只有最后一个对象保留了下来,其他的obj全都被置空回收了。即说明了在内存不足的情况下,软引用将会被自动回收。

对于弱引用,就像我们医药箱里的备用药,当我们需要药品时,我们会先看看医药箱里是否有备用药。如果医药箱里有足够的药品(内存足够),我们就可以使用备用药;但如果医药箱里的备用药不够了(内存不足),我们可能会去药店购买。在内存不足时,垃圾回收器可能会回收软引用对象,类似于我们在医药箱里的备用药被用完时去药店购买。

弱引用

JDK1.2之后,用java.lang.ref.WeakReference来表示弱引用。弱引用与软引用类似,但强度更弱。即使内存足够,只要没有强引用指向一个对象,垃圾回收器就可以随时回收该对象。弱引用适用于需要临时引用对象的场景,如临时缓存或临时存储对象。也可以用于解决对象之间的循环引用问题,避免内存泄漏。

对于上述示例中,我们将数组赋值给弱引用

private static final List<Object> list = Lists.newArrayList();  
  
public static void main(String[] args) {  
    IntStream.range(0, 10).forEach(i -> {  
        byte[] buff = new byte[1024 * 1024];  
        WeakReference<byte[]> sr = new WeakReference<>(buff);  
        list.add(sr);  
    });  
  
    System.gc(); // 主动通知垃圾回收  
  
    list.forEach(l -> {  
        Object obj = ((WeakReference<?>) l).get();  
        System.out.println("Object: " + obj);  
    });  
}

执行结果发现所有的对象都是null,即都被回收了。

image.png

对于弱引用,就像我们正在旅行,使用一张一次性地图。我们只在需要导航时使用地图,一旦旅行结束,我们就不再需要地图了。这时我们可以选择扔掉地图,类似于弱引用,在垃圾回收器运行时,无论内存是否充足,对象都可能被回收。

虚引用

在 JDK1.2之后,用java.lang.ref.PhantomReference类来表示虚引用。虚引用是最弱的引用类型,它几乎对对象没有任何影响,不能通过虚引用获取对象,也不能通过它来阻止对象被垃圾回收。从源码中可以看出它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。

image.png

虚引用可以用于在对象被回收时进行后续操作,如对象资源释放或日志记录,常用于跟踪对象被垃圾回收的状态,执行一些清理工作。

而对于弱引用,就像我们去商店,商店入口处的门闩并不直接影响你进入房屋,但它会在有人进入或离开时发出声音,提醒你有人进店(欢迎光临)或者离开(欢迎再来)。类似地,虚引用并不直接影响对象的生命周期,但它可以在对象被回收时发出通知,让你有机会进行一些后续操作,比如资源释放或者记录日志。

引用队列

引用队列(ReferenceQueue)是Java中的一个特殊队列,用于配合软引用、弱引用和虚引用,实现更灵活的对象引用和回收管理。

引用队列的主要作用是跟踪对象的垃圾回收过程。当一个软引用、弱引用或虚引用指向的对象被垃圾回收器回收时,如果它们与一个引用队列关联,那么这些引用就会被自动加入到引用队列中。通过监视引用队列中的对象,我们可以了解到对象的回收状态,从而执行一些额外的操作,比如资源释放或日志记录等。

总结

Java中的四种引用类型各具特点,可根据程序需求选择合适的引用类型。强引用保证对象不被意外回收,软引用和弱引用用于实现缓存或解决内存敏感问题,而虚引用则用于对象回收后的通知和清理操作。合理使用引用类型可以更好地管理内存和避免内存泄漏问题。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等

标签:说一说,Java,对象,美团,回收,垃圾,引用,内存
From: https://www.cnblogs.com/coderacademy/p/18089036

相关文章

  • 详细解读JavaScript中的防抖(debounce)和节流(throttle)!!!
    在JavaScript中,防抖(debounce)和节流(throttle)是两种常用的技术,用于限制函数的执行频率,特别是在处理高频事件(如窗口的resize、scroll,输入框的keyup、mousedown等)时非常有用。防抖(debounce)防抖的基本思想是将多次执行变为最后一次执行。也就是说,在事件被触发后n秒内函数只能执......
  • JavaScript 中对象的浅拷贝(Shallow Copy)和深拷贝(Deep Copy)!!
    在JavaScript中,对象的拷贝可以分为浅拷贝(ShallowCopy)和深拷贝(DeepCopy)。理解这两者的区别是非常重要的,因为它们影响着拷贝后的对象与原始对象之间的关系。浅拷贝(ShallowCopy):浅拷贝只会拷贝对象的第一层属性。如果对象的属性是基本数据类型(例如,字符串,数字,布尔值等),那......
  • 基于JavaWeb的鲜牛奶订购系统—开题报告
    一、研究解决的问题本系统使用Java作为开发语言,基于JavaWebB/S架构进行设计,前端技术HTML+CSS+Vue,后端技术SpringBoot,数据库采用MySql,并在win10操作平台完成开发。系统用户角色分为订奶用户,供奶商家和管理员三个部分。其中,订奶用户可以浏览住址所在区的订奶信息,登录后拥有管理......
  • Java JDK 22全新发布 - 猫头虎博主带你一起深入了解JDK22!
    博主猫头虎的技术世界......
  • 蓝桥杯Java ABC组 AcWing P1020 潜水员
    题目链接:https://www.acwing.com/problem/content/1022/#二维背包#Model#Favorite思路好题!可以让你思考各种背包问题中,对体积的定义不同,则初始化就不同本题求的是是至少需要体积VV......
  • Java中的代理模式(动态代理和静态代理)
    代理模式我们先了解一下代理模式:在开发中,当我们要访问目标类时,不是直接访问目标类,而是访问器代理类。通过代理类调用目标类完成操作。简单来说就是:把直接访问变为间接访问。这样做的最大好处就是:我们可以在代理类调用目标类之前和之后去添加一些预处理和后处理操作。来扩展......
  • JAVA对象、类和基本数据类型
    变量和标识符数学名词:变数或变量,是指没有固定的值,可以改变的数。变量以非数字的符号来表示,一般用拉丁字母。变量和常数是相反的。变量的用处在于能一般化描述指令的方式计算机解释:变量就是系统为程序分配的一块内存单元,用来储存各种类型的数据。根据所储存的数据类型不同,有......
  • Java面试相关问题
     一.MySql篇1优化相关问题1.1.MySql中如何定位慢查询? 慢查询的概念:在MySQL中,慢查询是指执行时间超过一定阈值的SQL语句。这个阈值是由long_query_time参数设定的,它的默认值是10秒1。也就是说,如果一条SQL语句的执行时间超过了long_query_time所设定的时间,那么这条SQL......
  • JAVA基本数据类型转换、关键字、转义字符
    基本数据类型转换自动类型转换:容量小的类型自动转换成容量大的数据类型byte,short,它们在计算时会转换int类型如果把int转换成float值,或者long转换成double值,不需要强制转换,但可能丢失精度publicclassMain{publicstaticvoidmain(String[]args){byteb......
  • 深入解析Mybatis-Plus框架:简化Java持久层开发(十二)
    ......