Java中的各种引用类型以及部分引用的相关例子

这篇具有很好参考价值的文章主要介绍了Java中的各种引用类型以及部分引用的相关例子。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引用类型

在Java中,引用类型主要有四种,分别是:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。这些类型通常与垃圾回收机制有关,用来描述对象的生命周期和可达性。下面详细介绍每一种引用类型:

  1. 强引用(Strong Reference)
    强引用是最常见的引用类型,当在代码中创建一个对象并赋值给一个引用变量时,这个引用就是强引用。例如:

    String str = new String("Java");
    

    只要强引用还存在,垃圾回收器永远不会回收被引用的对象。强引用可能导致内存泄漏,因为即使对象已经不再需要了,只要强引用还在,对象就不会被回收。

  2. 软引用(Soft Reference)
    软引用是为了解决内存敏感的缓存问题而设计的。通过java.lang.ref.SoftReference类可以创建软引用。垃圾回收器在系统内存不足时会回收这些对象。软引用通常用于实现内存敏感的高速缓存,例如,图片缓存。软引用可以让缓存的对象在内存充足时被保留,而在内存不足时被回收。

    SoftReference<String> softReference = new SoftReference<>(new String("Java"));
    
  3. 弱引用(Weak Reference)
    弱引用通过java.lang.ref.WeakReference类实现。弱引用不阻止它的对象被垃圾回收器回收。垃圾回收器一旦发现只有弱引用指向的对象,不管当前内存空间足够与否,都会回收它。弱引用比软引用更弱,它主要用于实现没有阻止垃圾收集的引用链,例如,常见于元数据、查找大型结构的关键等。

    WeakReference<String> weakReference = new WeakReference<>(new String("Java"));
    
  4. 虚引用(Phantom Reference)
    虚引用是最弱的一种引用类型,通过java.lang.ref.PhantomReference类实现。一个具有虚引用的对象,跟没有引用一样,在任何时候都可能被垃圾回收器回收。设置虚引用的唯一目的是在这个对象被回收时收到一个系统通知。虚引用必须和引用队列(ReferenceQueue)联合使用。虚引用主要用于跟踪对象被垃圾回收的活动,例如,确保对象完全销毁后进行某些特定资源的清理。

    PhantomReference<String> phantomReference = new PhantomReference<>(new String("Java"), new ReferenceQueue<>());
    

弱引用

合理的使用弱引用可以解决部分内存泄漏的问题。

ThreadLocal

ThreadLocal是Java中多线程编程中一个重要的类。它能够将值存储在当前线程中,不与其他线程共享,同时让编码者能够跨越多个层级获取到该值。比如在Web服务中我们可以使用ThreadLocal存储请求的id,从而使得该次请求中所有的日志输出都携带上请求id,方便后续的异常排查;又或是如同Spring Transaction的实现一样,将Connection存储在ThreadLocal,让一个线程绑定一个Connection,实现事务机制。

ThreadLocal实现解析:
ThreadLocal的本身的实现就是弱引用使用的一个经典案例。
Java中的各种引用类型以及部分引用的相关例子
Java中的各种引用类型以及部分引用的相关例子
上面是ThreadLocal的get方法,可以看到它先是获取当前的线程之后再获取了线程中的ThreadLocalMap实例,接着从该Map中获取到具体的值。也就是说ThreadLocal实际上是通过每一个线程中存储一个单独的ThreadLocalMap实例来实现的。

接下来查看ThreadLocalMap的源码:
Java中的各种引用类型以及部分引用的相关例子
ThreadLocalMap是ThreadLocal的一个内部类,它的Entry继承自WeakReference,结合Entry的构造方法可以发现Entry是持有了一个指向ThreadLocal实例的弱引用并且使用该弱引用作为Key。如此实现,当外部没有指向该ThreadLocal的软引用、强引用之后,该ThreadLocal实例将会被直接回收。这样做带来的一个好处就是,在我们不需要该ThreadLocal对象之后,这个ThreadLocal能够及时的被GC回收,保证不会因此导致内存泄漏。

有一个经典问题:ThreadLocal是如何导致内存泄漏的?
首先ThreadLocal自身提供了remove方法,上面说了ThreadLocalMap的实现保证了程序不会因为ThreadLocal导致内存泄漏,那这个问题是不是与上面所说的冲突了?其实不然,从前面的代码可以看到ThreadLocalMap的Entry.key虽然是弱引用,但是Entry.value并不是弱引用。当ThreadLocal被GC回收之后,并且我们在回收之前没有显示调用remove方法,我们便无法访问到对应的Entry,从而将该Entry删除,那么始终便存在一条这样的引用链路Thread->ThreadLocalMap->Entry->value,在线程被结束之前该value以及该Entry无法被回收,若是该线程无法没及时结束,那么就有可能导致内存泄漏。

可能会有人问为何不将Entry以及Entry.value也实现为弱引用?答案也很简单,若是实现为弱引用,那么很多情况下通过ThreadLocal获取一开始存入的值都将为null,在非特殊情况下,
ThreadLocal的作用可以说是等于零。

综上所述,在编码的时候,只要合理的调用remove方法其实就能够避免因ThreadLocal导致的内存泄漏。

WeakHashMap

Java中还有其他地方也使用到了弱引用,比如WeakHashMap。

  • 相比ThreadLocal,WeakHashMap也是通过将对象的软引用作为entry.key,但是与ThreadLocal最大的不同就是它同时使用了ReferenceQueue,ReferenceQueue提供了在引用对象被GC回收时,通知给程序的功能,WeakHashMap借此实现了在get等方法调用时,自动删除被回收key所对应条目的功能。

使用案例:

import java.util.WeakHashMap;

public class WeakCache {
    private final WeakHashMap<Key, BigObject> weakMap = new WeakHashMap<>();

    public void put(Key key, BigObject object) {
        weakMap.put(key, object);
    }

    public BigObject get(Key key) {
        return weakMap.get(key);
    }

    // Key类用于映射的键
    class Key {
        // ...
    }

    // BigObject类是一个大对象,占用大量内存
    class BigObject {
        // ...
    }
}

WeakCache 类中,通过WeakHashMap 实现了一个弱引用键的缓存。当Key没有在其他地方被强引用时,这个Key-Value映射随时都可能被自动移除。文章来源地址https://www.toymoban.com/news/detail-850184.html

到了这里,关于Java中的各种引用类型以及部分引用的相关例子的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ElasticSearch进阶:多种查询操作,各种ES查询以及在Java中的实现

    目录 前言 1 词条查询 1.1 等值查询-term 1.2 多值查询-terms 1.3 范围查询-range 1.4 前缀查询-prefix 1.5 通配符查询-wildcard 2 复合查询 2.1 布尔查询 2.2 Filter查询 3 聚合查询 3.1 最值、平均值、求和 3.2 去重查询 3.3 分组聚合 3.3.1 单条件分组 3.3.2 多条件分组 3.4 过滤聚合 ElasticSearch 第一篇

    2024年02月02日
    浏览(38)
  • 程序的各种段以及堆栈相关问题

    C中一般有三种变量: 局部变量 全局变量 静态变量 C中一般有五个内存段: 代码段 也叫文本段,包含frequently executed code 通常是只读的(未了避免程序被错误改写) 代码段是不包含程序变量(如局部变量、全局变量)的 代码段可以包含只读的string字符,如包含代码 printf(\\\"H

    2024年02月05日
    浏览(26)
  • 【六大锁策略-各种锁的对比-Java中的Synchronized锁和ReentrantLock锁的特点分析-以及加锁的合适时机】

    阅读该文章之前要了解,锁策略是为了解决什么问题 多线程带来的的风险-线程安全的问题的简单实例-线程不安全的原因 提示:以下是本篇文章正文内容,下面案例可供参考 锁冲突是指两个线程对一个对象加锁,产生了阻塞等待。 乐观锁 假设数据一般情况下不会产生并发冲

    2024年02月15日
    浏览(43)
  • 重温C#中的值类型和引用类型

    在C#中,数据类型分为 值类型 和 引用类型 两种。 引用类型变量存储的是数据的引用,数据存储在数据堆中,而值类型变量直接存储数据。对于引用类型,两个变量可以引用同一个对象。因此,对一个变量的操作可能会影响另一个变量引用的对象。对于值类型,每个变量都有

    2024年02月15日
    浏览(37)
  • C++引用的相关定义以及使用场景与指针之前的区别

    🌏博客主页: 主页 🔖系列专栏: C++ ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。 引用的特性: 引用在定义时必

    2024年02月03日
    浏览(30)
  • JavaScript中的数据类型可以分为两类:原始类型和引用类型。

    JavaScript中的数据类型可以分为两类:原始类型和引用类型。 JavaScript中的原始类型有五种,分别是: 数字(Number):表示数字,包括整数和浮点数。 字符串(String):表示文本字符串。 布尔值(Boolean):表示逻辑上的真或假。 undefined:表示未定义的值。 null:表示空值。

    2024年01月24日
    浏览(36)
  • sql知识点复习以及项目中的例子

      常见的聚合函数: avg(),求平均值; sum() 求和; count(),计算和; min()求最小值; max()求最大值 聚合函数的应用场景: COUNT:用于统计指定列的行数,可以用于统计表中的记录数或者去重后的记录 数。 SUM:用于计算指定列的总和,可以用于计算某个时间段

    2023年04月19日
    浏览(41)
  • 快速清除Java项目中引用的各种包的安全漏洞

    Java项目中往往会使用很多第三方类库或开源类库。在完成项目开发,并准备上线时,经常需要做漏洞扫描。一旦扫描出的上千的漏洞往往会让我们不知所措。如果上线要求非常严格,针对漏扫结果,我们需要逐个jar包查找有没有对应的升级或补丁。而即便我们找到了升级Ja

    2024年02月11日
    浏览(23)
  • Java 四种引用类型(强引用、软引用、弱引用、虚引用)

    在 Java 中,基本类型和引用类型是两种不同的数据类型 基本类型直接表示数值、字符或布尔值,直接将值存储在内存,包括 byte、short、int 、long、float 、double、char、boolean 引用类似 C 语言中的指针,在内存中存储的是对象的引用而不是对象本身,通过这个引用可以操作对象

    2024年02月05日
    浏览(37)
  • 3dmax2021 中的各种显示相关如何设置?

    这就是一盏灯和两盏灯的差别 注意,视图模式不要选择standard标准模式,在这种模式下,即使是两盏灯,也会是这样子 这个稍微显得复杂,要经过两步,第一步在View菜单下选择viewport config,如图设置: 然后后,选择Customize菜单中的Customize User Interface: 这个功能可以省去我们

    2024年02月05日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包