Java中引用的概念

序言:

Java对于引用概念经历过两个时期,JDK1.2之前和JDK1.2之后。在JDK1.2以前Java对于引用的定义很传统,如果reference类型的数据中存储的数值指定是另外一块内存的起始地址,就称为这块内存代表着一个引用.这个定义过于狭隘,在整个应用中对象就存在两种状态存在引用或者不存在。这样会产生一定的问题,对于我们应用系统存在于一些对象只在初始化使用一次(却被其它对象引用),存在一些对象是不经常被调用(也被其它对象引用),也存在一些对象是一直在被使用的。当内存资源足够的情况下,不会发生太大问题,一旦出现内存中资源紧缺,我们需要释放一些对象内存。我们肯定是期望这些只在初始化使用一次和不经常被调用的对象的内存释放出来。但是因为被引用的对象是不会被垃圾回收器给回收掉的,所以终止就会导致系统因内存而直接崩溃,这种现象我将其类比于以前的大锅饭(不论干不干活都有饭吃)。

针对于上述JDK1.2的状况Java在JDK1.2之后对于引用的概念细致化分为4种(让我们可以针对不同情况使用不同引用),分别为强引用(Strong Reference),软引用(Soft Reference),弱引用(Weak Reference),虚引用 (Phantom Reference).4种引用强度依次降低。java对于这四种引用的具体实现在java.lang.ref包下,如图所示。

类所在包情况:

完整类图:

2.1:强引用(Strong Reference)

java中最普遍存在的引用类型.例如我们在Object obj = new Object()时就会产生的这样的引用。在java应用中只要上述情况的强引用存在,垃圾回收器永远不会回收被引用对象。

2.2:软引用(Soft Reference)

软引用:可引用一些有用但却不是应用必须的对象,对于这些对象在应用内存资源不足(垃圾回收器已经进行过一次回收了),要发生内存溢出之前,垃圾回收器将这些对象列为可回收对象范围之内再进行第二次回收,如果此次回收内存资源还是不足,那么就会发生内存溢出溢出。在java中使用SoftReference类来实现软引用。

2.3:弱引用(Weak Reference)

弱引用和软引用的功能一样也是用以引用非必须的对象。但是它的强度比软引用更弱,不论jvm中内存是否充足,垃圾回收器在下一次垃圾回收的时候这些被弱引用的对象肯定会被回收。在java中使用WeakReference类来实现弱引用。

2.4:虚引用(Phantom Reference)

虚引用在四种引用类型中最弱的一种引用关系,它的存在对于对象的生存时间没有丝毫影响,也无法通过虚引用来获取一个对象的实例。它实际的作用是在当该对象被垃圾回收是可触发系统通知。在java中使用PhantomReference来实现虚引用。

2.5:引用队列 ReferenceQueue

通过查看SoftReference,WeakReference,PhantomReference三种不同类可以看到在初始化方法中都有ReferenceQueue的的影子,ReferenceQueue在其中充当的角色是用以实现在被三种引用对象被gc时,其它线程可以通过ReferenceQueue可以进行感知并作出一些处理。(后续有代码测试 )

3:代码应用:

3.1:强引用

/**
 *强引用测试 为测试效果使用-Xmx10m -Xms10m
 */
public class StrongReferenceTest {

    //堆内
    public  static List<Byte[]> datas = new ArrayList<>();

    public static void main(String[] args) throws Exception {

        for (int i=0;i<10;i++ ) {
            datas.add(new Byte[1024*1024]);
            Thread.sleep(100);
            //触发gc
            System.gc();
        }

    }
}

运行结果:

3.2:软引用测试

/**
 * 软引用测试 设置内存大小为10m -Xmx10m -Xms10m
 * @author fangyuan
 */
public class SoftReferenceTest {

    public  static List<SoftReference<Byte[]>> datas = new ArrayList<>();

    public static void main(String[] args) throws Exception {

        for (int i=0;i<5;i++ ) {
            SoftReference<Byte[]> srf = new SoftReference<Byte[]>(new Byte[1024*512]);
            datas.add(srf);
        }
        //验证数据是不是被gc掉
        for (SoftReference softReference:datas) {
            System.out.println(softReference.get());
        }

    }
}

运行结果:

从上述结果可以看到:当jvm感觉内存不足时(内存不足的定义是什么?与堆内存大小以及该引用对象get的时间都有关系),垃圾回收器会回收软引用对象。

3.3 弱引用测试

/**
 * 弱引用测试  这里不设置最大内存大小
 * @author fangyuan
 */
public class WeakReferenceTest {

    public  static List<WeakReference<Byte[]>> datas = new ArrayList<>();

    public static void main(String[] args) throws Exception {

        for (int i=0;i<5;i++ ) {
            WeakReference<Byte[]> weakReference = new WeakReference<Byte[]>(new Byte[1024*512]);
            datas.add(weakReference);
        }

        //验证数据
        for (WeakReference weakReference:datas) {
            System.out.print(weakReference.get());
        }

        System.out.println();

        //触发gc
        System.gc();

        Thread.sleep(1000);

        //验证数据是不是被gc掉
        for (WeakReference weakReference:datas) {
            System.out.print(weakReference.get());
        }

    }
}

结果:

测试结果发现,弱引用对象只能存活到下一次垃圾回收器开始工作之前,无关于现有系统内存大小。

3.4 虚引用

//源码
public class PhantomReference<T> extends Reference<T> {

    //虚引用无法通过get方法获取对应实例对象
    public T get() {
        return null;
    }

    //初始化 配合ReferenceQueue做到被引用对象被gc之后能过通知用户线程 进行额外处理
    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}

测试代码当gc虚引用时,用户线程得知并作出处理:

/**
 * 测试ReferenceQueue 与PhantomReference 不设置内存最大值
 * @author fangyuan
 */
public class PhantomReferenceTest {

    public  static List<PhantomReference<Byte[]>> datas = new ArrayList<>();

    public  static ReferenceQueue<Byte[]> referenceQueues = new ReferenceQueue<>();

    public static void main(String[] args) throws Exception {

        //设置
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int count =0 ;
                while(true){
                    try {

                        Reference<? extends Byte[]> pr;
                        if ((pr = referenceQueues.remove()) !=null){
                            System.out.println("第"+(count++)+"个回收了对象"+pr);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }

            }
        });

        thread.setDaemon(true);
        thread.start();

        while (true) {
            PhantomReference<Byte[]> phantomReference = new PhantomReference<Byte[]>(new Byte[1024*1024],referenceQueues);
            datas.add(phantomReference);
        }

    }
}

测试结果:

<think>首先,用户的问题是:“我想了解Java中的弱引用概念及其使用 请问Java引用 是什么 如何使用”。我需要根据系统级指令和用户可见层的要求来回答。 系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 - 参考站内引用引用[1]、[2]、[3]是关于Java引用的内容。 用户的问题是关于Java引用的概念和使用,所以我需要基于提供的引用内容来构建回答。 回顾引用内容: - 引用[1]:介绍了弱引用的概念、优缺点、适用场景,以及Java中的语法。 - 引用[2]:详细说明了弱引用的语法和原理。 - 引用[3]:解释了弱引用引用队列的关系,以及WeakHashMap的使用。 我的回答结构应该清晰: 1. **介绍概念**:解释什么是Java引用。 2. **使用方法**:描述如何创建和使用弱引用。 3. **适用场景**:讨论弱引用在哪些场景下有用。 4. **优缺点**:简要提及弱引用的优缺点。 5. **示例**:提供代码示例来演示弱引用的使用。 6. **相关问题**:生成相关问题。 确保使用中文回答。 在回答中,引用段落末尾添加引用标识,如[^1]。 对于数学表达式:用户问题中没有明显的数学内容,但如果有需要,我会使用$...$或$$...$$。但在这个主题中,可能不太需要,除非涉及算法复杂度等。 生成相关问题:在回答结束时,添加“相关问题”部分。 现在,构建回答大纲: - **开头**:直接回答问题,解释Java引用是什么。 - **主体**: - 概念详解。 - 语法和使用方法。 - 适用场景。 - 优缺点。 - 代码示例。 - **结尾**:总结。 - **相关问题**:列出2-3个相关问题。 参考提供的引用: - 弱引用Java中的一种引用类型,允许对象在没有强引用时被垃圾回收。 - 语法:使用WeakReference类。 - 使用场景:如缓存、WeakHashMap。 - 引用队列:用于跟踪被回收的对象。 代码示例:从引用中提取,如WeakReference的使用。 确保回答真实可靠:基于标准Java知识。 最终回答结构: 1. Java引用的概念。 2. 如何使用弱引用。 3. 适用场景。 4. 优缺点。 5. 代码示例。 6. 相关问题。</think>### Java引用的概念与使用 Java 中的弱引用(Weak Reference)是一种特殊的引用类型,它允许对象在没有强引用(Strong Reference)指向时被垃圾回收器(GC)回收。这与强引用不同:强引用会阻止对象被回收,而弱引用则不会。弱引用主要用于实现内存敏感的缓存、避免内存泄漏等场景[^1]。下面我将逐步解释其概念、使用方法、适用场景、优缺点,并提供代码示例。 #### 1. **弱引用的概念** 在 Java 中,对象通过引用被访问。弱引用是 `java.lang.ref.WeakReference` 类的一个实例,它指向另一个对象,但不阻止该对象被垃圾回收。当 GC 运行时,如果对象只有弱引用指向它(没有强引用),则该对象会被标记为可回收,并在下一次 GC 中被清除。弱引用常用于实现“临时性”缓存,其中对象在内存不足时自动释放[^1][^2]。 弱引用的核心原理是:它不增加对象的引用计数,GC 会忽略弱引用,只检查强引用。如果一个对象仅被弱引用指向,GC 会将其回收,并将弱引用对象置为 `null`。弱引用通常与引用队列(`ReferenceQueue`)结合使用,用于跟踪被回收的对象[^3]。 #### 2. **如何使用弱引用** 在 Java 中使用弱引用的语法很简单,主要通过 `WeakReference` 类实现。以下是基本步骤: - **创建弱引用**:使用 `WeakReference` 包装一个对象。 - **访问对象**:通过 `get()` 方法获取弱引用指向的对象。如果对象已被回收,`get()` 返回 `null`。 - **结合引用队列**:可选地使用 `ReferenceQueue` 来监听对象回收事件。 **代码示例**: ```java import java.lang.ref.WeakReference; import java.lang.ref.ReferenceQueue; public class WeakReferenceExample { public static void main(String[] args) { // 创建强引用对象 String strongRef = new String("Hello, World!"); // 创建引用队列(可选) ReferenceQueue<String> queue = new ReferenceQueue<>(); // 创建弱引用,指向 strongRef 对象 WeakReference<String> weakRef = new WeakReference<>(strongRef, queue); // 访问对象:此时对象存在,输出 "Hello, World!" System.out.println("弱引用对象: " + weakRef.get()); // 移除强引用,只保留弱引用 strongRef = null; // 触发垃圾回收(实际应用中不手动调用,这里仅为演示) System.gc(); // 等待 GC 完成 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // 再次访问:对象已被回收,输出 null System.out.println("GC 后弱引用对象: " + weakRef.get()); // 检查引用队列:如果对象被回收,队列会收到通知 if (queue.poll() != null) { System.out.println("对象已被回收,引用队列收到通知"); } } } ``` **解释**: - 在这个示例中,`strongRef` 是强引用,初始时指向一个字符串对象。 - 创建 `weakRef` 弱引用指向同一对象。 - 当 `strongRef` 被置为 `null` 后,对象只有弱引用指向它。调用 `System.gc()` 模拟垃圾回收后,`weakRef.get()` 返回 `null`,表示对象已被回收。 - `ReferenceQueue` 用于监听回收事件:`queue.poll()` 检查是否有引用被加入队列(表示对象已回收)。 #### 3. **适用场景** 弱引用在以下场景中特别有用: - **缓存实现**:例如,实现内存敏感的缓存(如图片缓存)。当内存不足时,缓存对象自动释放,避免 OutOfMemoryError[^1][^2]。 - **避免内存泄漏**:在监听器或回调中,使用弱引用可以防止对象因强引用而无法回收,常见于 GUI 框架或事件处理[^3]。 - **WeakHashMap 应用**:Java 提供了 `WeakHashMap` 类,其键(Key)使用弱引用。当键对象没有强引用时,整个条目(Key-Value)自动移除,非常适合实现临时性映射[^3]。 ```java import java.util.WeakHashMap; public class WeakHashMapExample { public static void main(String[] args) { WeakHashMap<String, String> map = new WeakHashMap<>(); String key = new String("key"); map.put(key, "value"); // 移除强引用 key = null; System.gc(); // 触发 GC // 检查 map:条目可能已被移除 System.out.println("Map 大小: " + map.size()); // 可能输出 0 } } ``` - **资源管理**:在需要跟踪对象生命周期但不希望强占内存的场景,如数据库连接池[^1]。 #### 4. **优缺点** - **优点**: - 防止内存泄漏:弱引用不会阻止 GC 回收对象,特别适合管理临时对象[^2]。 - 简化缓存设计:结合 `WeakHashMap`,可以轻松实现自动清理的缓存[^3]。 - 提高内存效率:在内存敏感应用中,减少不必要的内存占用[^1]。 - **缺点**: - 不可靠性:对象随时可能被回收,因此 `get()` 方法可能返回 `null`,使用时需检查空值[^2]。 - 性能开销:引用队列的处理可能增加 GC 的负担,在高性能场景需谨慎使用[^3]。 - 不适合长期存储:弱引用对象不能保证持久存在,不能用于核心数据存储[^1]。 #### 5. **总结** Java引用是一种强大的工具,用于管理对象生命周期而不强占内存。核心是通过 `WeakReference` 类创建引用,并结合 `ReferenceQueue` 或 `WeakHashMap` 实现自动回收。它适用于缓存、资源管理等场景,但开发者需注意其不可靠性,确保代码健壮性[^1][^2][^3]。在实际开发中,优先考虑强引用和软引用(Soft Reference),仅在必要时使用弱引用
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值