首页 > 基础资料 博客日记
Java中的各种引用类型以及部分引用的相关例子
2024-04-13 18:00:03基础资料围观286次
引用类型
在Java中,引用类型主要有四种,分别是:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。这些类型通常与垃圾回收机制有关,用来描述对象的生命周期和可达性。下面详细介绍每一种引用类型:
-
强引用(Strong Reference)
强引用是最常见的引用类型,当在代码中创建一个对象并赋值给一个引用变量时,这个引用就是强引用。例如:String str = new String("Java");
只要强引用还存在,垃圾回收器永远不会回收被引用的对象。强引用可能导致内存泄漏,因为即使对象已经不再需要了,只要强引用还在,对象就不会被回收。
-
软引用(Soft Reference)
软引用是为了解决内存敏感的缓存问题而设计的。通过java.lang.ref.SoftReference
类可以创建软引用。垃圾回收器在系统内存不足时会回收这些对象。软引用通常用于实现内存敏感的高速缓存,例如,图片缓存。软引用可以让缓存的对象在内存充足时被保留,而在内存不足时被回收。SoftReference<String> softReference = new SoftReference<>(new String("Java"));
-
弱引用(Weak Reference)
弱引用通过java.lang.ref.WeakReference
类实现。弱引用不阻止它的对象被垃圾回收器回收。垃圾回收器一旦发现只有弱引用指向的对象,不管当前内存空间足够与否,都会回收它。弱引用比软引用更弱,它主要用于实现没有阻止垃圾收集的引用链,例如,常见于元数据、查找大型结构的关键等。WeakReference<String> weakReference = new WeakReference<>(new String("Java"));
-
虚引用(Phantom Reference)
虚引用是最弱的一种引用类型,通过java.lang.ref.PhantomReference
类实现。一个具有虚引用的对象,跟没有引用一样,在任何时候都可能被垃圾回收器回收。设置虚引用的唯一目的是在这个对象被回收时收到一个系统通知。虚引用必须和引用队列(ReferenceQueue)联合使用。虚引用主要用于跟踪对象被垃圾回收的活动,例如,确保对象完全销毁后进行某些特定资源的清理。PhantomReference<String> phantomReference = new PhantomReference<>(new String("Java"), new ReferenceQueue<>());
弱引用
合理的使用弱引用可以解决部分内存泄漏的问题。
ThreadLocal
ThreadLocal是Java中多线程编程中一个重要的类。它能够将值存储在当前线程中,不与其他线程共享,同时让编码者能够跨越多个层级获取到该值。比如在Web服务中我们可以使用ThreadLocal存储请求的id,从而使得该次请求中所有的日志输出都携带上请求id,方便后续的异常排查;又或是如同Spring Transaction的实现一样,将Connection存储在ThreadLocal,让一个线程绑定一个Connection,实现事务机制。
ThreadLocal实现解析:
ThreadLocal的本身的实现就是弱引用使用的一个经典案例。
上面是ThreadLocal的get方法,可以看到它先是获取当前的线程之后再获取了线程中的ThreadLocalMap实例,接着从该Map中获取到具体的值。也就是说ThreadLocal实际上是通过每一个线程中存储一个单独的ThreadLocalMap实例来实现的。
接下来查看ThreadLocalMap的源码:
ThreadLocalMap是ThreadLocal的一个内部类,它的Entry继承自WeakReference,结合Entry的构造方法可以发现Entry是持有了一个指向ThreadLocal实例的弱引用并且使用该弱引用作为Key。如此实现,当外部没有指向该ThreadLocal的软引用、强引用之后,该ThreadLocal实例将会被直接回收。这样做带来的一个好处就是,在我们不需要该ThreadLocal对象之后,这个ThreadLocal能够及时的被GC回收,保证不会因此导致内存泄漏。
有一个经典问题:ThreadLocal是如何导致内存泄漏的?
首先ThreadLocal自身提供了remove方法,上面说了ThreadLocalMap的实现保证了程序不会因为ThreadLocal导致内存泄漏,那这个问题是不是与上面所说的冲突了?其实不然,从前面的代码可以看到ThreadLocalMap的Entry.key虽然是弱引用,但是Entry.value并不是弱引用。当ThreadLocal被GC回收之后,并且我们在回收之前没有显示调用remove方法,我们便无法访问到对应的Entry,从而将该Entry删除,那么始终便存在一条这样的引用链路Thread->ThreadLocalMap->Entry->value
,在线程被结束之前该value以及该Entry无法被回收,若是该线程无法没及时结束,那么就有可能导致内存泄漏。
可能会有人问为何不将Entry以及Entry.value也实现为弱引用?答案也很简单,若是实现为弱引用,那么很多情况下通过ThreadLocal获取一开始存入的值都将为null,在非特殊情况下,
ThreadLocal的作用可以说是等于零。
综上所述,在编码的时候,只要合理的调用remove方法其实就能够避免因ThreadLocal导致的内存泄漏。
WeakHashMap
Java中还有其他地方也使用到了弱引用,比如WeakHashMap。
- 相比ThreadLocal,WeakHashMap也是通过将对象的软引用作为entry.key,但是与ThreadLocal最大的不同就是它同时使用了ReferenceQueue,ReferenceQueue提供了在引用对象被GC回收时,通知给程序的功能,WeakHashMap借此实现了在get等方法调用时,自动删除被回收key所对应条目的功能。
使用案例:
import java.util.WeakHashMap;
public class WeakCache {
private final WeakHashMap<Key, BigObject> weakMap = new WeakHashMap<>();
public void put(Key key, BigObject object) {
weakMap.put(key, object);
}
public BigObject get(Key key) {
return weakMap.get(key);
}
// Key类用于映射的键
class Key {
// ...
}
// BigObject类是一个大对象,占用大量内存
class BigObject {
// ...
}
}
在WeakCache
类中,通过WeakHashMap
实现了一个弱引用键的缓存。当Key没有在其他地方被强引用时,这个Key-Value映射随时都可能被自动移除。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: