前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >我们为什么要使用Java的弱引用?

我们为什么要使用Java的弱引用?

原创
作者头像
喵手
发布2023-11-21 11:39:28
2700
发布2023-11-21 11:39:28
举报
文章被收录于专栏:Java进阶实战

theme: healer-readable

highlight: a11y-dark


哈喽,各位小伙伴们,你们好呀,我是喵手。

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

在Java开发中,内存管理一直是一个重要的话题。由于Java自动内存分配和垃圾回收机制的存在,我们不需要手动去管理内存,但是有时候我们却需要一些手动控制的方式来减少内存的使用。本文将介绍其中一种手动控制内存的方式:弱引用。

摘要

本文主要介绍了Java中弱引用的概念和使用方法。通过源代码解析和应用场景案例的分析,详细阐述了弱引用的优缺点以及适用的场景。最后,给出了类代码方法介绍和测试用例,并进行了全文小结和总结。

Java之弱引用

简介

弱引用是Java中一种较为特殊的引用类型,它与普通引用类型的最大不同在于,当一个对象只被弱引用所引用时,即使该对象仍然在内存中存在,也可能被垃圾回收器回收。

源代码解析

在Java中,弱引用的实现是通过WeakReference类来实现的。该类的定义如下:

代码语言:java
复制
public class WeakReference<T> extends Reference<T> {
    public WeakReference(T referent);
    public WeakReference(T referent, ReferenceQueue<? super T> q);
    public T get();
}

其中,构造方法分别是无参构造方法、有参构造方法和获取弱引用所引用的对象。

与强引用类型不同,弱引用不会对对象进行任何引用计数,也就是说,即使存在弱引用,对象的引用计数也不会增加。

  如下是部分源码截图:

在这里插入图片描述
在这里插入图片描述

应用场景案例

缓存

在开发中,缓存是一个很常见的场景。但是如果缓存中的对象一直存在,就会导致内存不断增加。这时,我们就可以考虑使用弱引用,在当缓存中的对象已经没有强引用时,该对象就会被回收。

代码语言:java
复制
Map<String, WeakReference<User>> cache = new HashMap<>();

public User getUser(String userId) {
    User user;
    // 判断是否在缓存中
    if (cache.containsKey(userId)) {
        WeakReference<User> reference = cache.get(userId);
        user = reference.get();
        if (user == null) {
            // 从数据库中读取
            user = db.getUserById(userId);
            // 加入缓存
            cache.put(userId, new WeakReference<>(user));
        }
    } else {
        // 从数据库中读取
        user = db.getUserById(userId);
        // 加入缓存
        cache.put(userId, new WeakReference<>(user));
    }
    return user;
}

上述代码中,我们在使用缓存时,首先判断该对象是否在缓存中。如果存在弱引用,我们先通过get()方法获取对象,如果对象不为null,则直接返回;如果对象为null,则说明该对象已经被回收了,此时需要从数据库中重新读取对象,并加入缓存。

监听器

在Java开发中,我们经常需要使用监听器。但是如果监听器存在强引用,当我们移除监听器时,由于其存在强引用,导致内存无法释放。使用弱引用则可以解决该问题。

代码语言:java
复制
public class Button {
    private List<WeakReference<ActionListener>> listeners = new ArrayList<>();

    public void addActionListener(ActionListener listener) {
        listeners.add(new WeakReference<>(listener));
    }

    public void removeActionListener(ActionListener listener) {
        listeners.removeIf(ref -> ref.get() == null || ref.get() == listener);
    }

    public void click() {
        for (WeakReference<ActionListener> ref : listeners) {
            ActionListener listener = ref.get();
            if (listener != null) {
                listener.perform();
            }
        }
    }
}

上述代码中,我们使用了一个List来保存所有的监听器。在添加监听器时,我们使用了WeakReference进行包装,以保证该监听器不会导致内存泄漏。在移除监听器时,通过removeIf()方法来匹配弱引用是否已经被回收,并且判断是否与指定的监听器相同。在触发事件时,我们通过get()方法获取弱引用所引用的对象,并判断是否为null,如果不为null,则执行监听器的perform()方法。

优缺点分析

优点

  1. 可以有效地降低内存占用;
  2. 适用于一些生命周期较短的对象,可以避免内存泄漏;
  3. 使用方便,只需要将对象包装为弱引用即可。

缺点

  1. 对象可能被提前回收,这可能会导致某些操作失败;
  2. 弱引用需要额外的开销,会对程序的性能产生一定的影响。

类代码方法介绍

WeakReference类

构造方法
代码语言:java
复制
public WeakReference(T referent);
public WeakReference(T referent, ReferenceQueue<? super T> q);

其中,第一个构造方法是无参构造方法,直接使用该方法会创建一个没有关联队列的弱引用。第二个构造方法需要传入一个ReferenceQueue队列,用于关联该弱引用。在目标对象被回收时,该队列会触发一个通知。

get()方法
代码语言:java
复制
public T get();

该方法用于获取弱引用所包装的对象,如果对象已经被回收,则返回null。

ReferenceQueue类

构造方法
代码语言:java
复制
public ReferenceQueue();

无参构造方法,直接使用该方法可以创建一个新的ReferenceQueue对象。

poll()方法
代码语言:java
复制
public Reference<? extends T> poll();

该方法用于获取ReferenceQueue队列中的下一个元素,如果队列为空,则返回null。

测试用例

测试代码演示

代码语言:java
复制
package com.example.javase.se.classes.weakReference;

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

/**
 * @Author ms
 * @Date 2023-11-05 21:43
 */
public class WeakReferenceTest {

    public static void main(String[] args) throws InterruptedException {
        testWeakReference();
        testCache();
        testButton();
    }

    public static void testWeakReference() throws InterruptedException {
        User user = new User("123", "Tom");
        WeakReference<User> weakReference = new WeakReference<>(user);
        user = null;
        System.gc();
        Thread.sleep(1000);
        assert weakReference.get() == null;
    }

    public static void testCache() throws InterruptedException {
        User user = new User("123", "Tom");
        Map<String, WeakReference<User>> cache = new HashMap<>();
        cache.put(user.getId(), new WeakReference<>(user));
        user = null;
        System.gc();
        Thread.sleep(1000);
        assert cache.get("123").get() == null;
    }

    public static void testButton() {
        Button button = new Button();
        ActionListener listener1 = new ActionListener();
        ActionListener listener2 = new ActionListener();
        button.addActionListener(listener1);
        button.addActionListener(listener2);
        button.click();
        listener1 = null;
        listener2 = null;
        System.gc();
        assert button.getListeners().get(0).get() == null;
        assert button.getListeners().get(1).get() == null;
        button.click();
    }

    static class User {
        private String id;
        private String name;

        public User(String id, String name) {
            this.id = id;
            this.name = name;
        }

        public String getId() {
            return id;
        }

        public String getName() {
            return name;
        }
    }

    static class ActionListener {
        public void perform() {
            System.out.println("Button clicked");
        }
    }

    static class Button {
        private List<WeakReference<ActionListener>> listeners = new ArrayList<>();

        public void addActionListener(ActionListener listener) {
            listeners.add(new WeakReference<>(listener));
        }

        public void click() {
            for (WeakReference<ActionListener> ref : listeners) {
                ActionListener listener = ref.get();
                if (listener != null) {
                    listener.perform();
                }
            }
        }

        public List<WeakReference<ActionListener>> getListeners() {
            return listeners;
        }
    }
}

测试结果

  根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。

在这里插入图片描述
在这里插入图片描述

测试代码分析

  根据如上测试用例,在此我给大家进行深入详细的解读一下测试代码,以便于更多的同学能够理解并加深印象。

此代码演示了 Java 中弱引用的使用场景,以及如何使用弱引用来实现缓存和事件监听器等功能。主要包括以下内容:

1.测试弱引用:定义一个 User 类,通过 WeakReference 弱引用来持有此对象,并在程序运行时将 User 对象设为 null,通过 System.gc() 手动触发 GC,验证弱引用是否被回收。

2.测试缓存:定义一个 Map 对象,将 User 对象通过 WeakReference 弱引用的形式存入,保留 User 对象的 ID,在后续程序运行时手动触发 GC,验证弱引用是否被回收。

3.测试事件监听器:定义一个 Button 类,通过 List\<WeakReference<ActionListener>> 弱引用来持有 ActionListener 对象,定义一个 addActionListener 方法,用于向 List 中添加 ActionListener 对象,定义一个 click 方法,用于触发 ActionListener 中的 perform 方法。在测试中,向 Button 中添加两个 ActionListener 对象,将它们设为 null,通过 System.gc() 手动触发 GC,验证弱引用是否被回收。

总的来说,弱引用主要用于缓存、事件监听器等场景,可以避免内存泄漏问题,但需要注意使用时的一些问题,比如弱引用被回收后,需要手动进行相应的处理等。

全文小结

本文介绍了Java中弱引用的概念和使用方法,通过源代码解析和应用场景案例的分析,详细阐述了弱引用的优缺点以及适用的场景。同时,也给出了类代码方法介绍和测试用例,最后进行了全文小结和总结。

总结

本文介绍了Java中弱引用的概念和使用方法,弱引用是一种较为特殊的引用类型,与普通引用类型不同的是,当一个对象只被弱引用所引用时,即使该对象仍然在内存中存在,也可能被垃圾回收器回收。

弱引用主要适用于一些生命周期较短的对象,可以有效地降低内存占用。同时,在一些需要监听器、缓存等场景中,使用弱引用可以避免内存泄漏。

在使用弱引用时,我们可以使用WeakReference类来实现,并通过get()方法获取弱引用所包装的对象。同时,我们也可以使用ReferenceQueue类来关联弱引用,当目标对象被回收时,该队列会触发一个通知。

但是弱引用也有其缺点,例如对象可能被提前回收,这可能会导致某些操作失败,同时弱引用也需要额外的开销,会对程序的性能产生一定的影响。

因此,在使用弱引用时,我们需要根据具体场景具体分析,权衡其优缺点,选择合适的引用类型来进行内存管理。

... ...

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

... ...

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 摘要
  • Java之弱引用
    • 简介
      • 源代码解析
        • 应用场景案例
          • 缓存
          • 监听器
        • 优缺点分析
          • 优点
          • 缺点
        • 类代码方法介绍
          • WeakReference类
          • ReferenceQueue类
        • 测试用例
          • 测试代码演示
          • 测试结果
          • 测试代码分析
        • 全文小结
        • 总结
        • 文末
        相关产品与服务
        腾讯云代码分析
        腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,助力维护团队卓越代码文化。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档