侧边栏壁纸
  • 累计撰写 29 篇文章
  • 累计创建 38 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Java引用类型:游乐园关系学大师课

16uni
2025-06-15 / 0 评论 / 0 点赞 / 25 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

在Java游乐园中,引用类型是设施和游客之间复杂关系的终极指南。就像游乐园的VIP系统,不同级别的"关系"决定了设施何时被拆除(垃圾回收)。准备好深入探索这个奇妙世界了吗?系好安全带,我们的"引用关系过山车"即将出发!

一、强引用:终身VIP至尊关系

深度解析:

强引用是Java世界的默认关系,就像游乐园的终身VIP:

  • 永久特权:只要VIP游客还在游乐园,对应设施就永远安全

  • 无视压力:即使游乐园内存爆满(OutOfMemoryError),VIP设施也纹丝不动

  • 手动解除:只有VIP主动放弃特权(引用置null),设施才会被标记为可拆除

class VIPLounge {
    String name;
    int capacity;
    
    VIPLounge(String name, int capacity) {
        this.name = name;
        this.capacity = capacity;
        System.out.println("VIP休息区已建造: " + name);
    }
    
    void serveVIP(String vipName) {
        System.out.println(vipName + "正在享受VIP服务");
    }
    
    protected void finalize() throws Throwable {
        System.out.println("⚠️ VIP休息区被拆除: " + name);
        super.finalize();
    }
}
​
public class StrongReferenceMasterclass {
    public static void main(String[] args) {
        // 创建终身VIP关系
        VIPLounge goldenLounge = new VIPLounge("黄金休息区", 10);
        
        // 终身VIP小明享受服务
        goldenLounge.serveVIP("小明");
        
        // 模拟内存压力(疯狂建造临时设施)
        try {
            System.out.println("\n==== 游乐园游客爆满 ====");
            List<VIPLounge> tempLounges = new ArrayList<>();
            for (int i = 1; i <= 1000; i++) {
                tempLounges.add(new VIPLounge("临时休息区#" + i, 5));
            }
        } catch (OutOfMemoryError e) {
            System.out.println("💥 内存溢出!游乐园已满员!");
        }
        
        // 检查VIP休息区状态
        System.out.println("\n==== 压力测试后 ====");
        System.out.println("黄金休息区状态: " + 
            (goldenLounge != null ? "完好无损" : "已被拆除"));
        
        // VIP主动放弃特权
        System.out.println("\n==== VIP放弃特权 ====");
        goldenLounge = null;  // 解除强引用关系
        
        // 请求垃圾回收(可能不会立即执行)
        System.out.println("呼叫垃圾回收工人...");
        System.gc();
        
        // 给GC一点时间(实际开发不要这样用)
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("程序结束");
    }
}

关键知识点:

  1. 强引用是最常见的引用类型(默认所有引用都是强引用)

  2. 只有对象没有任何强引用时才会被回收

  3. System.gc()只是建议JVM进行垃圾回收,不保证立即执行

  4. finalize()方法在对象被回收前调用(Java 9后已弃用,仅作演示)

二、软引用:黄金季卡会员关系

深度解析:

软引用就像购买季卡的游客:

  • 优先特权:在内存充足时,设施保持完好

  • 弹性策略:当内存不足时,设施可能被拆除

  • 优雅降级:拆除前系统会尝试其他回收方式

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
​
class SeasonalAttraction {
    String name;
    String season;
    
    SeasonalAttraction(String name, String season) {
        this.name = name;
        this.season = season;
        System.out.println("季节性设施开放: " + name + " (" + season + ")");
    }
    
    void operate() {
        System.out.println(name + "正在运营");
    }
    
    protected void finalize() {
        System.out.println("🚧 季节性设施关闭: " + name);
    }
}
​
public class SoftReferenceDeepDive {
    public static void main(String[] args) {
        // 创建软引用(夏季限定设施)
        SoftReference<SeasonalAttraction> summerSlide = 
            new SoftReference<>(new SeasonalAttraction("巨浪水滑梯", "夏季"));
        
        // 检查设施状态
        checkAttractionStatus("初始状态", summerSlide);
        
        // 第一次垃圾回收
        System.out.println("\n==== 第一次GC ====");
        System.gc();
        checkAttractionStatus("GC后", summerSlide);
        
        // 制造内存压力
        System.out.println("\n==== 制造内存压力 ====");
        try {
            List<byte[]> memoryHogs = new ArrayList<>();
            int counter = 0;
            while (true) {
                memoryHogs.add(new byte[1024 * 1024]); // 每次分配1MB
                System.out.println("已分配内存: " + (++counter) + "MB");
                
                // 每10MB检查一次设施状态
                if (counter % 10 == 0) {
                    checkAttractionStatus("压力测试中", summerSlide);
                }
            }
        } catch (OutOfMemoryError e) {
            System.out.println("\n💥 内存溢出!");
        }
        
        // 最终状态检查
        System.out.println("\n==== 最终状态 ====");
        checkAttractionStatus("内存溢出后", summerSlide);
    }
    
    private static void checkAttractionStatus(String phase, 
                                           SoftReference<SeasonalAttraction> ref) {
        SeasonalAttraction attraction = ref.get();
        if (attraction != null) {
            System.out.println(phase + ": " + attraction.name + " 仍在运营");
        } else {
            System.out.println(phase + ": 季节性设施已被关闭");
        }
    }
}

关键知识点:

  1. 软引用适合实现内存敏感缓存

  2. 垃圾回收器在抛出OOM前会回收所有软引用对象

  3. 使用SoftReference.get()获取对象(可能返回null)

  4. 实际回收时机取决于JVM实现和当前内存使用情况

三、弱引用:普通一日票关系

深度解析:

弱引用就像一日票游客:

  • 短暂特权:设施只能存活到下次垃圾回收

  • 无视内存:无论内存是否充足,GC一来就拆除

  • 快速回收:最激进的回收策略

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
​
class DailyShow {
    String name;
    String time;
    
    DailyShow(String name, String time) {
        this.name = name;
        this.time = time;
        System.out.println("每日表演安排: " + name + " @ " + time);
    }
    
    void perform() {
        System.out.println("表演开始: " + name);
    }
    
    protected void finalize() {
        System.out.println("🎭 表演结束: " + name);
    }
}
​
public class WeakReferenceMasterclass {
    public static void main(String[] args) {
        // 创建弱引用
        WeakReference<DailyShow> magicShow = 
            new WeakReference<>(new DailyShow("魔法秀", "14:00"));
        
        // 创建WeakHashMap(自动清理无引用的条目)
        Map<WeakReference<DailyShow>, String> showSchedule = new HashMap<>();
        showSchedule.put(magicShow, "主舞台");
        
        // 检查初始状态
        checkShowStatus("初始状态", magicShow);
        
        // 保持强引用
        DailyShow strongRef = magicShow.get();
        
        // 第一次GC(强引用仍在)
        System.out.println("\n==== 第一次GC(强引用存在)====");
        System.gc();
        checkShowStatus("GC后", magicShow);
        
        // 释放强引用
        System.out.println("\n==== 释放强引用 ====");
        strongRef = null;
        
        // 第二次GC
        System.out.println("\n==== 第二次GC(无强引用)====");
        System.gc();
        checkShowStatus("GC后", magicShow);
        
        // 检查WeakHashMap
        System.out.println("\n==== WeakHashMap状态 ====");
        System.out.println("演出计划条目数: " + showSchedule.size());
        System.out.println("魔法秀场地: " + showSchedule.get(magicShow));
    }
    
    private static void checkShowStatus(String phase, WeakReference<DailyShow> ref) {
        DailyShow show = ref.get();
        if (show != null) {
            System.out.println(phase + ": " + show.name + " 正常进行");
        } else {
            System.out.println(phase + ": 表演已结束");
        }
    }
}

关键知识点:

  1. 弱引用在下次GC时必定被回收(只要没有强引用)

  2. WeakHashMap是弱引应的经典应用:键被回收时自动移除条目

  3. 适合实现规范化映射(如字符串池)

  4. 常见于监听器模式和临时对象管理

四、虚引用:幽灵监控关系

深度解析:

虚引用就像游乐园的监控系统:

  • 无形存在:无法通过它访问设施

  • 精准监控:在对象被回收时收到通知

  • 终极清理:用于回收后的资源清理

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
​
class HauntedHouse {
    String name;
    long spookyEffectId; // 模拟原生资源
    
    HauntedHouse(String name) {
        this.name = name;
        this.spookyEffectId = allocateNativeResource();
        System.out.println("鬼屋建造完成: " + name + " [资源ID: " + spookyEffectId + "]");
    }
    
    // 模拟分配原生资源(如外部内存、文件句柄)
    private long allocateNativeResource() {
        return System.nanoTime(); // 模拟资源ID
    }
    
    // 清理原生资源
    private void releaseNativeResource() {
        System.out.println("释放幽灵特效资源: " + spookyEffectId);
    }
    
    // 资源清理回调
    public void cleanUp() {
        releaseNativeResource();
    }
}
​
public class PhantomReferenceDeepDive {
    public static void main(String[] args) throws InterruptedException {
        // 创建引用队列(拆除通知中心)
        ReferenceQueue<HauntedHouse> demolitionQueue = new ReferenceQueue<>();
        
        // 创建鬼屋和虚引用
        HauntedHouse hauntedHouse = new HauntedHouse("尖叫城堡");
        PhantomReference<HauntedHouse> phantomRef = 
            new PhantomReference<>(hauntedHouse, demolitionQueue);
        
        // 虚引用永远无法获取对象
        System.out.println("通过虚引用获取对象: " + phantomRef.get()); // null
        
        // 启动监控线程
        Thread cleanupThread = new Thread(() -> {
            try {
                // 阻塞等待拆除通知
                PhantomReference<?> ref = 
                    (PhantomReference<?>) demolitionQueue.remove();
                System.out.println("\n==== 收到拆除通知 ====");
                
                // 执行清理操作
                System.out.println("执行鬼屋拆除后清理...");
                
                // 在实际应用中,这里会调用清理方法
                // 例如:((HauntedHouseResourceCleaner) ref).cleanUp();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        cleanupThread.setDaemon(true);
        cleanupThread.start();
        
        // 保持鬼屋一段时间
        System.out.println("\n==== 鬼屋运营中 ====");
        Thread.sleep(2000);
        
        // 准备拆除(取消强引用)
        System.out.println("\n==== 关闭鬼屋 ====");
        hauntedHouse = null;
        
        // 请求垃圾回收
        System.out.println("呼叫垃圾回收工人...");
        System.gc();
        
        // 给监控线程时间处理
        Thread.sleep(3000);
        System.out.println("程序结束");
    }
}

关键知识点:

  1. 虚引用必须与ReferenceQueue一起使用

  2. get()方法始终返回null,无法获取对象

  3. 对象被回收前,虚引用会被加入引用队列

  4. 主要用途:

    • 管理堆外内存(DirectByteBuffer)

    • 精确控制资源释放

    • 对象回收跟踪

五、引用队列:游乐园调度中心

深度解析:

引用队列是游乐园的调度中心:

  • 回收通知:当对象被回收时,对应的引用会入队

  • 批量处理:可以批量处理回收通知

  • 多引用支持:支持软、弱、虚引用

import java.lang.ref.*;
import java.util.concurrent.*;
​
public class ReferenceQueueMasterclass {
    public static void main(String[] args) throws InterruptedException {
        // 创建引用队列和线程池
        ReferenceQueue<Attraction> queue = new ReferenceQueue<>();
        ExecutorService executor = Executors.newFixedThreadPool(2);
        
        // 创建监控线程
        executor.submit(() -> {
            System.out.println("监控线程启动");
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    // 阻塞等待回收通知
                    Reference<? extends Attraction> ref = queue.remove();
                    
                    if (ref instanceof SoftReference) {
                        System.out.println("[软引用] 设施回收: " + ref);
                    } else if (ref instanceof WeakReference) {
                        System.out.println("[弱引用] 设施回收: " + ref);
                    } else if (ref instanceof PhantomReference) {
                        System.out.println("[虚引用] 设施回收: " + ref);
                    }
                    
                    // 实际应用中可以执行清理操作
                }
            } catch (InterruptedException e) {
                System.out.println("监控线程终止");
            }
        });
        
        // 创建各种引用
        Attraction strongRef = new Attraction("终身VIP设施");
        SoftReference<Attraction> softRef = new SoftReference<>(
            new Attraction("黄金季卡设施"), queue);
        WeakReference<Attraction> weakRef = new WeakReference<>(
            new Attraction("一日票设施"), queue);
        PhantomReference<Attraction> phantomRef = new PhantomReference<>(
            new Attraction("幽灵监控设施"), queue);
        
        // 保持强引用一段时间
        System.out.println("保持强引用...");
        Thread.sleep(2000);
        
        // 释放强引用
        System.out.println("\n==== 释放强引用 ====");
        strongRef = null;
        
        // 请求GC
        System.out.println("呼叫GC...");
        System.gc();
        Thread.sleep(1000);
        
        // 创建内存压力(主要影响软引用)
        System.out.println("\n==== 创建内存压力 ====");
        try {
            List<byte[]> memoryPressure = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                memoryPressure.add(new byte[1024 * 1024]); // 1MB
            }
        } catch (OutOfMemoryError e) {
            System.out.println("内存压力创建完成");
        }
        
        // 再次GC
        System.out.println("\n==== 最终GC ====");
        System.gc();
        Thread.sleep(2000);
        
        // 关闭监控
        executor.shutdownNow();
        executor.awaitTermination(1, TimeUnit.SECONDS);
        System.out.println("程序结束");
    }
}
​
class Attraction {
    String name;
    public Attraction(String name) {
        this.name = name;
        System.out.println("设施创建: " + name);
    }
    protected void finalize() {
        System.out.println("设施回收: " + name);
    }
}

关键知识点:

  1. 引用队列是处理对象回收通知的核心机制

  2. 可以批量处理回收通知,提高效率

  3. 适合构建自动清理的缓存系统

  4. 监控线程应该设置为守护线程或可中断

六、引用类型对比表:游乐园关系大全

特性

强引用

软引用

弱引用

虚引用

回收时机

永不回收

内存不足时

下次GC

回收后通知

get()返回值

对象本身

对象或null

对象或null

永远null

内存敏感

引用队列

不支持

可选

可选

必须

典型用途

所有常规对象

内存缓存

规范化映射

资源清理

回收优先级

最低

中等

最高

游乐园比喻

终身VIP

黄金季卡

一日票

监控系统

七、引用类型实战:游乐园资源管理系统

import java.lang.ref.*;
import java.util.*;
import java.util.concurrent.*;
​
public class ThemeParkResourceManager {
    // 核心资源(强引用)
    private final RollerCoaster mainAttraction = new RollerCoaster("雷霆过山车");
    
    // 季节性资源(软引用)
    private SoftReference<WaterPark> waterParkRef;
    
    // 临时表演(弱引用)
    private final Map<String, WeakReference<LiveShow>> liveShows = new HashMap<>();
    
    // 原生资源监控(虚引用+队列)
    private final ReferenceQueue<SpecialEffectSystem> effectQueue = new ReferenceQueue<>();
    private final Map<PhantomReference<SpecialEffectSystem>, Runnable> effectCleaners = new HashMap<>();
    
    // 监控线程
    private final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor();
    
    public ThemeParkResourceManager() {
        // 初始化监控线程
        monitorExecutor.submit(this::monitorResources);
    }
    
    // 添加季节性设施
    public void openWaterPark(String season) {
        waterParkRef = new SoftReference<>(new WaterPark(season));
        System.out.println("水上乐园开放: " + season);
    }
    
    // 添加临时表演
    public void scheduleShow(String showName) {
        liveShows.put(showName, new WeakReference<>(new LiveShow(showName)));
        System.out.println("表演安排: " + showName);
    }
    
    // 添加特效系统(带清理钩子)
    public void installSpecialEffect(String effectName) {
        SpecialEffectSystem effect = new SpecialEffectSystem(effectName);
        PhantomReference<SpecialEffectSystem> phantomRef = 
            new PhantomReference<>(effect, effectQueue);
        
        // 注册清理函数
        effectCleaners.put(phantomRef, effect::cleanUpNativeResources);
        
        System.out.println("特效系统安装: " + effectName);
    }
    
    // 资源监控
    private void monitorResources() {
        System.out.println("资源监控启动");
        try {
            while (!Thread.currentThread().isInterrupted()) {
                // 检查水上乐园状态
                if (waterParkRef != null && waterParkRef.get() == null) {
                    System.out.println("[通知] 水上乐园已关闭");
                    waterParkRef = null;
                }
                
                // 检查虚引用队列
                Reference<?> ref = effectQueue.poll();
                if (ref != null) {
                    Runnable cleaner = effectCleaners.remove(ref);
                    if (cleaner != null) {
                        System.out.println("[特效清理] 执行资源释放");
                        cleaner.run();
                    }
                }
                
                // 检查临时表演
                liveShows.entrySet().removeIf(entry -> {
                    if (entry.getValue().get() == null) {
                        System.out.println("[通知] 表演已结束: " + entry.getKey());
                        return true;
                    }
                    return false;
                });
                
                // 稍作休息
                TimeUnit.SECONDS.sleep(1);
            }
        } catch (InterruptedException e) {
            System.out.println("资源监控终止");
        }
    }
    
    // 关闭资源管理器
    public void shutdown() {
        monitorExecutor.shutdownNow();
        System.out.println("资源管理器关闭");
    }
    
    // 模拟资源类
    static class RollerCoaster { /* ... */ }
    static class WaterPark { /* ... */ }
    static class LiveShow { /* ... */ }
    static class SpecialEffectSystem {
        private final String name;
        public SpecialEffectSystem(String name) { this.name = name; }
        public void cleanUpNativeResources() {
            System.out.println("清理特效系统资源: " + name);
        }
    }
}

八、引用类型高级话题

1. Reference对象的可达性

/*
 * 引用对象本身的可达性:
 * 
 *   强引用 -> 引用对象本身是强可达
 *   软引用 -> 引用对象本身是软可达
 *   弱引用 -> 引用对象本身是弱可达
 *   虚引用 -> 引用对象本身是虚可达
 * 
 * 这决定了引用对象本身何时被回收
 */

2. 引用与finalize()的交互

/*
 * 对象回收流程:
 * 1. 对象不可达时,被标记为可回收
 * 2. 如果有finalize()方法,对象被加入Finalizer队列
 * 3. Finalizer线程执行finalize()
 * 4. 对象真正被回收,相关引用加入队列
 * 
 * 注意:Java 9+已弃用finalize()
 */

3. 跨代引用问题

/*
 * 分代GC中的引用处理:
 * - 老年代引用年轻代对象:会阻止年轻代回收
 * - 使用记忆集(Remembered Set)记录跨代引用
 * - 软/弱/虚引用需要特殊处理
 */

九、引用类型最佳实践

  1. 缓存设计

    // 三级缓存策略
    public class SmartCache {
        // 一级缓存(强引用,最近使用)
        private Map<String, Object> strongCache = new LinkedHashMap<>(16, 0.75f, true) {
            protected boolean removeEldestEntry(Map.Entry eldest) {
                return size() > 100; // 限制大小
            }
        };
        
        // 二级缓存(软引用,常用数据)
        private Map<String, SoftReference<Object>> softCache = new HashMap<>();
        
        // 三级缓存(弱引用,历史数据)
        private Map<String, WeakReference<Object>> weakCache = new HashMap<>();
    }
  2. 资源清理模式

    public class NativeResourceHolder {
        private final ReferenceQueue<Resource> queue = new ReferenceQueue<>();
        private final Map<PhantomReference<Resource>, Runnable> cleaners = new HashMap<>();
        
        public Resource acquireResource() {
            Resource resource = new Resource();
            PhantomReference<Resource> ref = new PhantomReference<>(resource, queue);
            cleaners.put(ref, resource::cleanUp);
            return resource;
        }
        
        // 定期清理
        public void cleanUp() {
            Reference<?> ref;
            while ((ref = queue.poll()) != null) {
                Runnable cleaner = cleaners.remove(ref);
                if (cleaner != null) cleaner.run();
            }
        }
    }
  3. 内存泄漏检测

    public class LeakDetector {
        private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
        private final Map<String, WeakReference<Object>> references = new HashMap<>();
        
        public void register(Object obj, String context) {
            references.put(context, new WeakReference<>(obj, queue));
        }
        
        public void checkLeaks() {
            Reference<?> ref;
            while ((ref = queue.poll()) != null) {
                // 找到被回收的引用
                references.values().removeIf(wr -> wr == ref);
            }
            
            // 剩余的可能是内存泄漏
            references.forEach((ctx, ref) -> {
                if (ref.get() == null) {
                    System.out.println("潜在泄漏: " + ctx);
                }
            });
        }
    }

十、引用类型终极总结

强引用 "我的设施我做主,除非我主动放手" 适用:核心业务对象,长期存在的实体

软引用 "内存充足我享受,内存不足我先走" 适用:缓存系统,大对象临时存储

弱引用 "GC一来我就走,从不留恋不停留" 适用:规范化映射,临时元数据

虚引用 "我不碰设施分毫,只收拆除通知书" 适用:精准资源清理,回收跟踪

现在你已成为Java引用类型的游乐园关系学大师!记住这些关键点

  1. 强引用是默认关系,其他引用需要显式创建

  2. 软引用适合缓存,弱引用适合临时数据

  3. 虚引用+引用队列是资源清理的黄金组合

  4. 实际开发中优先使用Java内置工具(如WeakHashMap)

// 毕业挑战:创建一个自管理资源的游乐园设施
public class SelfManagingAttraction {
    private final String name;
    private final ReferenceQueue<SelfManagingAttraction> queue;
    private final PhantomReference<SelfManagingAttraction> phantomRef;
    
    public SelfManagingAttraction(String name) {
        this.name = name;
        this.queue = new ReferenceQueue<>();
        this.phantomRef = new PhantomReference<>(this, queue);
        
        // 启动监控线程
        new Thread(this::monitor).start();
        System.out.println("设施开放: " + name);
    }
    
    private void monitor() {
        try {
            // 等待回收通知
            phantomRef.get(); // 永远返回null
            queue.remove();   // 阻塞等待
            
            // 执行清理
            cleanUpResources();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    private void cleanUpResources() {
        System.out.println("设施拆除清理: " + name);
        // 实际资源清理代码...
    }
    
    public void operate() {
        System.out.println(name + " 正在运营");
    }
}

理解引用类型,就是掌握了内存管理的魔法钥匙!

0

评论区