Java资源管理和引用体系的使用详解

2025-05-13 14:50

本文主要是介绍Java资源管理和引用体系的使用详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Java资源管理和引用体系的使用详解》:本文主要介绍Java资源管理和引用体系的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教...

Java中的引用类型不仅是内存管理的概念,更是资源管理的重要工具。合理使用不同类型的引用可以有效地管理内存敏感资源,防止内存泄漏和资源浪费。

1、java的引用体系

如下图所示,可分为四种:强引用、软引用、弱引用和虚引用。

Java资源管理和引用体系的使用详解

以下是对四种引用的类比,可以让大家有个更好的理解。

Java资源管理和引用体系的使用详解

1、强引用 (Strong Reference)

基本特性

Object obj = new Object(); // 这就是强引用
  • 默认引用类型:所有普通对象引用都是强引用。
  • GC行为:只要强引用存在,对象就不会被回收。
  • 内存泄漏:无意保留的强引用是内存泄漏的主因。

代码示例:

public class StrongReferenceExample {
    private List<String> cache = new ArrayList<>();
    
    public void addToCache(String data) {
        cache.add(data); // 强引用保留对象
    }
    
    public void clearCache() {
        cache.clear(); // 必须显式清除
    }
}

2、软引用 (Soft Reference)

1.基本特性

SoftReference<Object> softRef = new SoftReference<>(new Object());

GC行为

  • 内存充足时:保留对象
  • 内存不足时:回收对象(在OOM之前)
  • 缓存特性:适合实现内存敏感缓存

2.核心特点

  • 动态调整缓存大小
  • 系统内存充足时,可以缓存更多数据以提高性能
  • 当内存紧张时,自动清理部分缓存,避免内存溢出

优先级管理

  • 对缓存项设置优先级(如LRU最近最少使用)
  • 内存不足时优先清理低优先级数据

3.响应内存压力

  • 监听JVM内存使用情况(如 Runtime.getRuntime().freeMemory())。
  • 或使用 Java.lang.ref.SoftReference(软引用)让GC在内存不足时自动回收。
  • 实现方式

Java资源管理和引用体系的使用详解

实战示例:基于内存压力softReference的图片缓存

public class ImageCache {
    private final Map<String, SoftReference<Bitmap>> cache = new HashMap<>();
    
    public Bitmap getImage(String key) {
        SoftReference<Bitmap> ref = cache.get(key);
        return ref != null ? ref.get() : null;
    }
    
    public void putImage(String key, Bitmap image) {
        cache.put(key, new SoftReference<>(image));
    }
}

基于Runtime.freeMemoy()来实现。

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;

public class MemorySensitiveCache<K, V> {
    private final Map<K, SoftReference<V>> cache = new HashMap<>();

    // 存入缓存(使用软引用,内存不足时可被GC回收)
    public void put(K key, V value) {
        cache.put(key, new SoftReference<>(value));
    }

    // 从缓存读取(可能返回null如果已被GC回收)
    public V get(K key) {
        SoftReference<V> ref = cache.get(key);
        return (ref != null) ? ref.get() : null;
    }

    // 示例:内存监控线程
    public void startMemoryMonitor() {
        new Thread(() -> {
            while (true) {
                long freeMem = Runtime.getRuntime().freeMemory();
                if (freeMem < 1024 * 1024) { // 如果剩余内存 < 1MB
                    System.out.println("内存紧张,清理缓存...");
                    cache.clear();
                }
                try { Thread.sleep(1000); } catch (InterruptedException e) {}
            }
 http://www.chinasem.cn       }).start();
    }
}

3、弱引用 (Weak Reference)

3.1、基本特性

  • GC行为下次GC时立即回收(无论内存是否充足)
  • 规范映射:适合存储元数据等辅助信息
WeakReference<Object> weakRef = new WeakReferenjsce<>(new Object());

代码示例:

import java.util.WeakHashMap;

public class WeakHashMapExample {
    public static void main(String[] args) {
        WeakHashMap<Object, String> map = new WeakHashMap<>();
        
        Object key = new Object();
        map.put(key, "Value");
        
        System.out.println("GC 前: " + map.size()); // 1
        
        key = null; // 断开强引用
        System.gc(); // 触发 GC
        
       mxkxOh System.out.println("GC 后: " + map.size()); // 0(Entry 被自动清理)
    }
}

4、虚引用 (Phantom Reference)

基本特性

ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);

GC行为

  • 对象变得虚引用可达
  • 引用加入队列
  • 最终回收内存

特殊限制get()方法永远返回null

主要用途:精确控制资源清理时机

5、常见问题

Q1: WeakHashMap的key是弱引用,那value呢?

A: 只有key是弱引用,value是强引用。需要确保value不间接引用key,否则会内存泄漏。

Q2: 虚引用get()为什么返回null?

A: 这是设计使然,虚引用仅用于跟踪回收事件,不应再访问将死对象。

Q3: 软引用和弱引用谁更适合缓存?

A: 软引用适合主缓存(内存敏感),弱引用适合辅助缓存(立即释放)。

四种引用的区别和联系如下图所示:

Java资源管理和引用体系的使用详解

2、finalize

finalize:垃圾回收的终结机制。

2.1、所属领域

  • 垃圾回收(Garbage Collection) 的补充机制。
  • 属于 java.lang.Object 的基础方法,与 JVM 的 GC 流程相关。

2.2、作用

  • 在对象被垃圾回收前,JVM 会调用 finalize() 方法(如果重写了该方法),用于执行最后的清理操作(如关闭非内存资源)。

2.3、问题

  • 不可靠:JVM 不保证 finalize() 的执行时机(甚至可能不执行)。
  • 性能开销:拖慢 GC 效率(对象需要至少两次 GC 才能被回收)。
  • 已被废弃:Java 9 开始标记为 @Deprecated,推荐用 java.lang.ref.CleanerAutoCloseable 替代。

2.4、使用场景

想象你租了一间房:

  • 正常情况:退租时主动交还钥匙(相当于 close() 方法)
  • 忘记还钥匙:房东最后会自己来收钥匙(相当于 finalize()
  • 风险:房东可能很久后才来,期编程间钥匙被别人捡到可能不安全

代码示例:

public class Room {
    private static final Set<String> LEAKED_KEYS = new HashSet<>();
    private String roomKey;

    public Room(String key) {
        this.roomKey = key;
        LEAKED_KEYS.add(key); // 登记钥匙
        System.out.println("拿到钥匙进入房间: " + key);
    }

    // 主动还钥匙(推荐方式)
    public void returnKey() {
        if (roomKey != null) {
            System.out.println("主动归还钥匙: " + roomKey);
            LEAKED_KEYS.remove(roomKey); // 注销钥匙
            roomKey = null;
        }
    }

    @Override
    protected void finalize() throws Throwable {
        if (roomKey != null) {
            System.out.println("警告!钥匙未被归还: " + roomKey + " (内存泄漏)");
            LEAKED_KEYS.remove(roomKey); // 清理登记
        }
    }

    public static void main(String[] args) {
        // 好租客
        Room goodRoom = new Room("A101");
        goodRoom.returnKey();

        // 坏租客
        new Room("B202"); // 直接不保存引用
        
        System.gc();
        try { Thread.sleep(1000); } catch (Exception e) {}
        
        System.out.println("未归还的钥匙: " + LEAKED_KEYS);
    }
}

输出:

拿到钥匙进入房间: A101
主动归还钥匙: A101
拿到钥匙进入房间: B202
警告!钥匙未被归还: B202 (内存泄漏)
未归还的钥匙: []

结论

  • finalize() 只会在 GC 回收对象时执行,而 System.gc() 只是建议 GC 运行,不保证立即执行。
  • finalize() 不保证一定执行,如果程序China编程退出前 GC 没运行,它就不会被调用。
  • 不应该依赖 finalize() 管理资源(如文件、数据库连接),而应该使用 try-with-resources 或手动 close()
  • 同步调用 finalize() 会阻塞 GC 线程,降低性能。

3、try-with-resources

属于java的资源管理 → try-with-resources + AutoCloseable。

3.1、所属领域

  • 资源管理(Resource Management),尤其是需要显式释放的资源(如文件、数据库连接、网络套接字)。
  • 基于 AutoCloseable 接口(Java 7 引入),属于语法糖(Syntactic Sugar)。

3.2、作用

  • 自动在 try 代码块结束后调用资源的 close() 方法,避免资源泄露
  • 比手动 try-catch-finally 更简洁安全。

3.3、优势

  • 确定性释放:资源离开 try 块立即关闭。
  • 代码简洁:减少嵌套和样板代码。
  • 异常友好:支持抑制异常(Suppressed Exceptions)。

代码示例:

// 实现 AutoCloseable,用 try-with-resources 确保资源释放
class Resource implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("Resource closed!");
    }
}

public class Main {
    public static void main(String[] args) {
        try (Resource r = new Resource()) {
            // 使用资源
        } // 自动调用 close()
    }
}

finalize和try-with-resources的区别如下图所示:

Java资源管理和引用体系的使用详解

替代 finalize 的方案:

  • CleanerPhantomReference:提供更可控的清理机制(但复杂度高)。
  • AutoCloseable + try-with-resources:首选方案。

资源管理的最佳实践

  • 优先实现 AutoCloseable 接口,而非依赖 finalize
  • 对于必须手动管理的资源(如原生内存),结合 ByteBuffer.allocateDirectCleaner 使用。

4、Cleaner

(Java 9+)java.lang.ref.Cleaner。

如下图所示 :由phant虚引用和队列来实现。

Java资源管理和引用体系的使用详解

4.1、Cleaner 的优势

确定性更强:通过虚引用(PhantomReference)和引用队列实现

更安全:清理操作在独立线程执行,不影响主线程

性能更好:不干扰正常垃圾回收流程

可控性高:可以显式注册/取消清理操作

4.2、底层机制

  • 引用类型:使用 PhantomReference(虚引用)
  • 引用队列:被清理对象进入引用队列时触发清理
  • 守护线程:专门的清理线程处理队列中的对象

代码示例如下:

// Cleaner 内部实现伪代码
public class Cleaner {
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    private final PhantomReference<Object> phantomRef;
    private final Runnable cleanupAction;
    
    public static Cleaner create() {
        return new Cleaner();
    }
    
    public Cleanable register(Object obj, Runnable action) {
        phantomRef = new PhantomReference<>(obj, queue);
        this.cleanupAction = action;
        startCleanerThread();  // 启动监控线程
        return new CleanableImpl();
    }
    
    private void startCleanerThread() {
        Thread thread = new Thread(() -> {
            while (true) {
                Reference<?> ref = queue.remove(); // 阻塞等待
                if (ref == phantomRef) {
                    cleanupAction.run();
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
}

4.3、执行流程

Java资源管理和引用体系的使用详解

import java.lang.ref.Cleaner;

public class CleanerDemo {
    private static final Cleaner cleaner = Cleaner.create();

    static class Resource implements Runnable {
        @Override
        public void run() {
            System.out.println("Cleaner action executed!");
        }
    }

    public static void main(String[] args) {
        Resource resource = new Resource();
        cleaner.register(new Object(), resource); // 注册清理动作
        System.gc();
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
    }
}

相比较于finalize和cleaner的区别:

Java资源管理和引用体系的使用详解

总结

通过合理组合这些引用类型,可以构建出既高效又安全的资源管理系统。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持China编程(www.chinasem.cn)。

这篇关于Java资源管理和引用体系的使用详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154606

相关文章

ubuntu系统使用官方操作命令升级Dify指南

《ubuntu系统使用官方操作命令升级Dify指南》Dify支持自动化执行、日志记录和结果管理,适用于数据处理、模型训练和部署等场景,今天我们就来看看ubuntu系统中使用官方操作命令升级Dify的方... Dify 是一个基于 docker 的工作流管理工具,旨在简化机器学习和数据科学领域的多步骤工作流。

SpringBoot实现二维码生成的详细步骤与完整代码

《SpringBoot实现二维码生成的详细步骤与完整代码》如今,二维码的应用场景非常广泛,从支付到信息分享,二维码都扮演着重要角色,SpringBoot是一个非常流行的Java基于Spring框架的微... 目录一、环境搭建二、创建 Spring Boot 项目三、引入二维码生成依赖四、编写二维码生成代码五

C语言中的常见进制转换详解(从二进制到十六进制)

《C语言中的常见进制转换详解(从二进制到十六进制)》进制转换是计算机编程中的一个常见任务,特别是在处理低级别的数据操作时,C语言作为一门底层编程语言,在进制转换方面提供了灵活的操作方式,今天,我们将深... 目录1、进制基础2、C语言中的进制转换2.1 从十进制转换为其他进制十进制转二进制十进制转八进制十进

SpringBoot快速搭建TCP服务端和客户端全过程

《SpringBoot快速搭建TCP服务端和客户端全过程》:本文主要介绍SpringBoot快速搭建TCP服务端和客户端全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录TCPServerTCPClient总结由于工作需要,研究了SpringBoot搭建TCP通信的过程

Java如何根据文件名前缀自动分组图片文件

《Java如何根据文件名前缀自动分组图片文件》一大堆文件(比如图片)堆在一个目录下,它们的命名规则遵循一定的格式,混在一起很难管理,所以本文小编就和大家介绍一下如何使用Java根据文件名前缀自动分组图... 目录需求背景分析思路实现代码输出结果知识扩展需求一大堆文件(比如图片)堆在一个目录下,它们的命名规

Java中 instanceof 的用法详细介绍

《Java中instanceof的用法详细介绍》在Java中,instanceof是一个二元运算符(类型比较操作符),用于检查一个对象是否是某个特定类、接口的实例,或者是否是其子类的实例,这篇文章... 目录引言基本语法基本作用1. 检查对象是否是指定类的实例2. 检查对象是否是子类的实例3. 检查对象是否

Java中的内部类和常用类用法解读

《Java中的内部类和常用类用法解读》:本文主要介绍Java中的内部类和常用类用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录内部类和常用类内部类成员内部类静态内部类局部内部类匿名内部类常用类Object类包装类String类StringBuffer和Stri

C++类和对象之初始化列表的使用方式

《C++类和对象之初始化列表的使用方式》:本文主要介绍C++类和对象之初始化列表的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C++初始化列表详解:性能优化与正确实践什么是初始化列表?初始化列表的三大核心作用1. 性能优化:避免不必要的赋值操作2. 强

MySQL中的两阶段提交详解(2PC)

《MySQL中的两阶段提交详解(2PC)》:本文主要介绍MySQL中的两阶段提交(2PC),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录引言两阶段提交过程sync_binlog配置innodb_flush_log_at_trx_commit配置总结引言在Inn

Python MCPInspector调试思路详解

《PythonMCPInspector调试思路详解》:本文主要介绍PythonMCPInspector调试思路详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录python-MCPInspector调试1-核心知识点2-思路整理1-核心思路2-核心代码3-参考网址