Java中的引用
# Java中的引用
# 引用的存在理由
Java是面向对象的语言,对象通常存储在堆内存中。如果直接操作对象,可能意味着每次操作都需要在栈上创建对象的副本,这显然效率低下,尤其是对于大对象来说。所以引用应该是一种间接访问对象
的方式,避免复制开销,同时方便内存管理。
引用存在的必要性。直接操作对象在Java中可能不可行,因为对象的大小不固定,而栈空间有限,所以通过引用来指向堆中的对象地址,这样可以更灵活地管理内存。引用还允许动态内存分配,对象生命周期管理,比如垃圾回收机制就是基于引用来判断对象是否可达。
引用就是一个地址,该地址是对象在堆(heap)中的具体位置。通过该引用(地址),可以找到一个对象,找到就意味着可以进行操作对象。
# 为什么不能直接操作对象?
Java中对象通常存储在堆内存中,而堆内存的分配和回收由JVM动态管理。如果直接操作对象(例如在方法栈中传递整个对象),会导致以下问题:
- 性能开销:对象可能占用较大内存,直接传递会导致频繁的内存复制(类似C++中的按值传递)。
- 内存管理困难:对象生命周期无法灵活控制,可能导致内存泄漏或过早释放。
- 动态性缺失:无法实现多态、对象共享等面向对象特性。
通过引用(即指向堆内存中对象的指针),可以避免直接操作对象的物理内存,实现轻量级的对象访问。
# 引用的好处
间接访问对象 引用存储的是对象的地址(类似指针),而非对象本身,避免了直接操作对象的内存开销。
动态内存管理 通过引用,JVM可以自动回收未被引用的对象(垃圾回收),无需手动释放内存。
对象共享 多个引用可以指向同一个对象,实现数据共享(例如多个线程操作同一个对象)。
灵活的生命周期控制 不同类型的引用允许开发者精细控制对象的回收策略(如缓存、弱关联等)。
# 引用与GC
在Java程序运行中,引用常常与GC打交道。因没有的对象会被清理,相应的,该对象对应的引用也要被清理。
在 Java 中,引用(Reference) 决定了对象与垃圾回收器(GC)的交互方式。其中ThreadLocalMap
的 Entry
设计涉及弱引用和强引用,理解它们的差异对避免内存泄漏至关重要。
# 4 种引用类型
Java中引用的类型可以分为4种。分别是:强引用、软引用、弱引用和虚引用。每种引用的不同在于垃圾回收时的处理方式。
强引用最常见,只要存在,对象就不会被回收。软引用在内存不足时会被回收,适合缓存。弱引用一旦没有强引用指向对象,就会被回收,同样用于缓存,比如WeakHashMap。虚引用主要用于跟踪对象被回收的状态,必须和引用队列一起使用。
什么需要这么多类型的引用,直接都用强引用不行吗?一个地址而已。
# 设计4种的原因
Java的四种引用类型(强引用、软引用、弱引用、虚引用)通过不同的回收策略,解决了对象生命周期管理的多样化需求:
引用类型 | 特点 | 垃圾回收时机 | 典型应用场景 |
---|---|---|---|
强引用 (Strong Reference) | 默认引用类型,Object obj = new Object(); | 只要强引用存在,对象永远不会被回收。 | 普通对象操作。 |
软引用 (Soft Reference) | 通过SoftReference 类实现。 | 内存不足时会被回收。 | 实现缓存(如图片缓存)。 |
弱引用 (Weak Reference) | 通过WeakReference 类实现。 | 无论内存是否充足,只要没有强引用,立即回收。 | 缓存(如WeakHashMap )、避免内存泄漏。 |
虚引用 (Phantom Reference) | 通过PhantomReference 类实现。 | 对象被回收时,仅收到通知,无法通过虚引用访问对象。 | 管理堆外内存(如NIO的DirectBuffer)、资源清理。 |
# 强引用的扩展
在 Java 中,强引用(Strong Reference) 是默认的引用类型,也是我们日常编程中最常使用的引用。它的回收规则非常简单,但直接影响内存管理的关键逻辑:
- 强引用的对象只要还存在至少一个强引用并且这些引用都是可达的,那么这个对象就不会被垃圾回收。
- 要让一个强引用的对象能够被回收,必须确保没有任何强引用指向它,可以通过将引用设为
null
或者让引用超出其作用域来实现。 - 即便如此,具体的垃圾回收行为仍然取决于JVM的具体实现以及当前的内存使用情况。调用
System.gc()
只是建议JVM进行垃圾回收,并不能保证立即执行。
1.显式断开引用
将引用赋值为 null
,主动切断强引用链:
obj = null; // 对象此时可能被回收
2.引用超出作用域 当强引用变量离开其作用域(例如方法栈帧销毁),引用自动失效:
void example() {
Object localObj = new Object(); // 强引用
} // 方法结束,localObj 超出作用域,对象可能被回收
2
3
3.对象不再被任何强引用关联 即使存在其他类型的引用(如软引用、弱引用),只要没有强引用指向对象,对象仍会被回收。
# 代码示例
强引用 普通对象的默认引用,确保对象只要被需要就不会被回收。
Object obj = new Object(); // 强引用
1软引用 适合缓存场景,内存不足时自动释放,避免OOM。
SoftReference<byte[]> cache = new SoftReference<>(new byte[1024]); byte[] data = cache.get(); // 可能返回null
1
2弱引用 防止内存泄漏(如监听器、临时映射)。
WeakReference<Listener> weakListener = new WeakReference<>(listener);
1虚引用
用于跟踪对象被回收的状态(需配合ReferenceQueue
)。
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(obj, queue);
2