京东6.18大促主会场领京享红包更优惠

 找回密码
 立即注册

QQ登录

只需一步,快速开始

90%的Java程序员都不完全懂的四种引用:强、软、弱、虚

2025-12-10 12:04| 发布者: 搬梯子摘星星| 查看: 81| 评论: 0

摘要: Java 提供了四种不同强度的引用类型,用于精细控制对象的生命周期和垃圾回收行为。这些引用类型从强到弱分别是:强引用、软引用、弱引用和虚引用。1. 强引用 (Strong Reference)定义与特点强引用是 Java 中最常见、

Java 提供了四种不同强度的引用类型,用于精细控制对象的生命周期和垃圾回收行为。这些引用类型从强到弱分别是:强引用软引用弱引用虚引用

90%的Java程序员都不完全懂的四种引用:强、软、弱、虚

1. 强引用 (Strong Reference)

定义与特点

强引用是 Java 中最常见、最默认的引用类型。只要强引用存在,垃圾回收器就永远不会回收被引用的对象。

关键特性:

  • 对象不会被垃圾回收,即使内存不足时 JVM 宁愿抛出 OutOfMemoryError
  • 引用关系:对象可达 → 不会被回收
  • 日常编程中 99% 的使用场景都是强引用

示例代码

public class StrongReferenceExample {    public static void main(String[] args) {        // 创建强引用        Object obj = new Object();  // 强引用                // 对象可以通过强引用正常访问        System.out.println(obj.toString());                // 显式设置为null,断开强引用        obj = null;                // 此时对象变为不可达,可以被垃圾回收        System.gc();  // 建议JVM进行垃圾回收(但不保证立即执行)    }}

2. 软引用 (Soft Reference)

定义与特点

软引用描述一些"还有用但非必需"的对象。只有在内存不足时,垃圾回收器才会回收软引用指向的对象。

关键特性:

  • 内存充足时:对象不会被回收
  • 内存不足时:对象会被回收(在抛出 OutOfMemoryError 之前)
  • 常用于实现内存敏感的缓存

示例代码

import java.lang.ref.SoftReference;public class SoftReferenceExample {    public static void main(String[] args) {        // 创建一个大对象        byte[] largeObject = new byte[1024 * 1024 * 10]; // 10MB                // 创建软引用        SoftReference<byte[]> softRef = new SoftReference<>(largeObject);                // 断开强引用,只保留软引用        largeObject = null;                // 尝试获取软引用对象        byte[] retrieved = softRef.get();        if (retrieved != null) {            System.out.println("对象未被回收,大小: " + retrieved.length + " bytes");        } else {            System.out.println("对象已被回收(内存不足)");        }                // 模拟内存不足情况(实际应用中通过分配大量内存来触发)        try {            byte[] memoryHog = new byte[1024 * 1024 * 100]; // 尝试分配100MB        } catch (OutOfMemoryError e) {            System.out.println("内存不足,软引用可能被回收");        }                // 再次检查        if (softRef.get() == null) {            System.out.println("确认:软引用对象已被回收");        }    }}

3. 弱引用 (Weak Reference)

定义与特点

弱引用比软引用的生命周期更短。无论内存是否充足,只要发生垃圾回收,弱引用指向的对象就会被回收。

关键特性:

  • 下一次垃圾回收时对象就会被回收
  • 常用于实现规范化映射(如 WeakHashMap)或监控对象生命周期
  • 适合做临时缓存避免内存泄漏

示例代码

import java.lang.ref.WeakReference;public class WeakReferenceExample {    public static void main(String[] args) {        // 创建对象        String data = new String("重要数据");                // 创建弱引用        WeakReference<String> weakRef = new WeakReference<>(data);                System.out.println("GC前: " + weakRef.get());                // 断开强引用        data = null;                // 强制垃圾回收        System.gc();                // 给GC一点时间        try {            Thread.sleep(100);        } catch (InterruptedException e) {            e.printStackTrace();        }                // 检查对象是否被回收        if (weakRef.get() == null) {            System.out.println("对象已被垃圾回收");        } else {            System.out.println("对象仍然存在: " + weakRef.get());        }    }}

4. 虚引用 (Phantom Reference)

定义与特点

虚引用是最弱的一种引用关系。无法通过虚引用来获取对象实例,主要用于跟踪对象被垃圾回收的状态。

关键特性:

  • 无法通过 get() 方法获取对象(总是返回 null)
  • 必须与 ReferenceQueue 一起使用
  • 对象被回收时,虚引用会被加入到引用队列
  • 用于在对象被回收时执行清理操作

示例代码

import java.lang.ref.PhantomReference;import java.lang.ref.ReferenceQueue;public class PhantomReferenceExample {    public static void main(String[] args) {        // 创建引用队列        ReferenceQueue<Object> queue = new ReferenceQueue<>();                // 创建对象        Object resource = new Object();        System.out.println("原始对象: " + resource);                // 创建虚引用        PhantomReference<Object> phantomRef = new PhantomReference<>(resource, queue);                // 虚引用的get()方法总是返回null        System.out.println("虚引用get(): " + phantomRef.get()); // 输出null                // 断开强引用        resource = null;                // 启动监控线程        Thread monitorThread = new Thread(() -> {            try {                // 阻塞等待,直到有引用进入队列                PhantomReference<?> ref = (PhantomReference<?>) queue.remove();                System.out.println("检测到对象已被回收,可以执行清理操作");            } catch (InterruptedException e) {                e.printStackTrace();            }        });        monitorThread.start();                // 强制垃圾回收        System.gc();                try {            monitorThread.join(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

引用队列 (ReferenceQueue)

引用队列与软引用、弱引用、虚引用配合使用,当引用的对象被垃圾回收后,引用本身会被加入到引用队列中。

综合示例

import java.lang.ref.*;public class ReferenceQueueExample {    public static void main(String[] args) {        ReferenceQueue<Object> queue = new ReferenceQueue<>();                // 创建不同引用的对象        Object strongObj = new Object();        SoftReference<Object> softRef = new SoftReference<>(new Object(), queue);        WeakReference<Object> weakRef = new WeakReference<>(new Object(), queue);        PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);                System.out.println("初始状态:");        System.out.println("强引用: " + strongObj);        System.out.println("软引用: " + softRef.get());        System.out.println("弱引用: " + weakRef.get());        System.out.println("虚引用: " + phantomRef.get());                // 断开强引用,保留各种引用        strongObj = null;                // 强制垃圾回收        System.gc();                // 监控引用队列        new Thread(() -> {            try {                int count = 0;                while (count < 3) { // 预期会收到3个引用                    Reference<?> ref = queue.remove(1000); // 等待1秒                    if (ref != null) {                        count++;                        if (ref instanceof SoftReference) {                            System.out.println("软引用进入队列");                        } else if (ref instanceof WeakReference) {                            System.out.println("弱引用进入队列");                        } else if (ref instanceof PhantomReference) {                            System.out.println("虚引用进入队列");                        }                    }                }            } catch (InterruptedException e) {                e.printStackTrace();            }        }).start();    }}

实际应用场景

1. 缓存实现(软引用)

import java.lang.ref.SoftReference;import java.util.concurrent.ConcurrentHashMap;public class ImageCache {    private final ConcurrentHashMap<String, SoftReference<byte[]>> cache =         new ConcurrentHashMap<>();        public void putImage(String key, byte[] imageData) {        cache.put(key, new SoftReference<>(imageData));    }        public byte[] getImage(String key) {        SoftReference<byte[]> ref = cache.get(key);        if (ref != null) {            byte[] data = ref.get();            if (data != null) {                return data; // 缓存命中            } else {                cache.remove(key); // 清理已回收的引用            }        }        return null; // 缓存未命中    }        public void clear() {        cache.clear();    }}

2. 防止内存泄漏(弱引用 + WeakHashMap)

import java.util.Map;import java.util.WeakHashMap;public class ObjectRegistry {    private final Map<Object, String> registry = new WeakHashMap<>();        public void register(Object obj, String name) {        registry.put(obj, name);    }        public String getName(Object obj) {        return registry.get(obj);    }        // 当对象没有被其他强引用时,会自动从WeakHashMap中移除}

3. 资源清理(虚引用)

import java.lang.ref.PhantomReference;import java.lang.ref.ReferenceQueue;public class ResourceManager {    private static class CleanupReference extends PhantomReference<Object> {        private final Runnable cleanupTask;                public CleanupReference(Object resource, ReferenceQueue<? super Object> queue,                                Runnable cleanupTask) {            super(resource, queue);            this.cleanupTask = cleanupTask;        }                public void cleanup() {            if (cleanupTask != null) {                cleanupTask.run();            }        }    }        private final ReferenceQueue<Object> queue = new ReferenceQueue<>();    private final Map<CleanupReference, Object> references = new java.util.HashMap<>();        public void registerResource(Object resource, Runnable cleanup) {        CleanupReference ref = new CleanupReference(resource, queue, cleanup);        references.put(ref, null);                // 启动清理线程        startCleanupThread();    }        private void startCleanupThread() {        Thread cleanupThread = new Thread(() -> {            while (true) {                try {                    CleanupReference ref = (CleanupReference) queue.remove();                    ref.cleanup();                    references.remove(ref);                } catch (InterruptedException e) {                    break;                }            }        });        cleanupThread.setDaemon(true);        cleanupThread.start();    }}

总结对比表

特性

强引用

软引用

弱引用

虚引用

回收时机

永不回收

内存不足时

下次GC时

不确定,用于跟踪

get()返回值

对象本身

对象(未回收时)

对象(未回收时)

总是null

引用队列

不支持

可选

可选

必须使用

主要用途

普通对象引用

内存敏感缓存

临时缓存、防泄漏

资源清理跟踪

内存敏感度

不敏感

高敏感

非常敏感

无关

最佳实践与注意事项

1. 合理选择引用类型

// 根据需求选择合适的引用类型public class ReferenceChoice {    // 长期存在的核心数据 - 使用强引用    private final Map<String, User> userCache = new HashMap<>();        // 图片等大对象缓存 - 使用软引用    private final Map<String, SoftReference<byte[]>> imageCache = new HashMap<>();        // 临时性的监听器 - 使用弱引用防止内存泄漏    private final Map<EventListener, WeakReference<EventListener>> listeners = new HashMap<>();}

2. 避免常见陷阱

public class ReferencePitfalls {    public void commonMistakes() {        // 错误:弱引用被强引用持有,失去意义        String data = new String("data");        WeakReference<String> weakRef = new WeakReference<>(data);        // data 强引用仍然存在,weakRef 指向的对象不会被回收                // 正确:及时断开强引用        WeakReference<String> correctRef = new WeakReference<>(new String("data"));        // 或者        String temp = new String("data");        WeakReference<String> weakRef2 = new WeakReference<>(temp);        temp = null; // 及时断开强引用    }}

要是能把这些引用类型用对了,就能帮咱们编出更高效、更稳定的 Java 应用程序。尤其是在那些得精准控制内存使用的情况里,这方法特别管用。

90%的Java程序员都不完全懂的四种引用:强、软、弱、虚


查看详情:https://www.toutiao.com/article/7582071566893122111
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

QQ|手机版|小黑屋|梦想之都-俊月星空 ( 粤ICP备18056059号 )|网站地图

GMT+8, 2025-12-16 05:44 , Processed in 0.036903 second(s), 17 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2025 Discuz! Team.

返回顶部