本文主要是介绍Java资源管理和引用体系的使用详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《Java资源管理和引用体系的使用详解》:本文主要介绍Java资源管理和引用体系的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教...
Java中的引用类型不仅是内存管理的概念,更是资源管理的重要工具。合理使用不同类型的引用可以有效地管理内存敏感资源,防止内存泄漏和资源浪费。
1、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在内存不足时自动回收。 - 实现方式
实战示例:基于内存压力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: 软引用适合主缓存(内存敏感),弱引用适合辅助缓存(立即释放)。
四种引用的区别和联系如下图所示:
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.Cleaner
或AutoCloseable
替代。
2.4、使用场景
想象你租了一间房:
代码示例:
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的区别如下图所示:
替代 finalize
的方案:
Cleaner
或PhantomReference
:提供更可控的清理机制(但复杂度高)。AutoCloseable
+try-with-resources
:首选方案。
资源管理的最佳实践:
- 优先实现
AutoCloseable
接口,而非依赖finalize
。 - 对于必须手动管理的资源(如原生内存),结合
ByteBuffer.allocateDirect
和Cleaner
使用。
4、Cleaner
(Java 9+)java.lang.ref.Cleaner。
如下图所示 :由phant虚引用和队列来实现。
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、执行流程
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资源管理和引用体系的使用详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!