1. 强引用
JVM宁愿死,都不会回收。如果堆内存被强引用占满,JVM会OOMError、甚至崩溃,但就是不会去回收强引用。强引用就是这么强。
2. 软引用
JVM堆内存不足的时候回收。
主要用于缓存中场景。
例如: 在一个图片加载程序中,可以将已经加载过的图片用软引用保存起来; 在一个数据分析程序中,可以将部分处理过的数据用软引用保存,以便在需要时快速访问,但又不会一直占用大量内存。
SoftHashMap使用软引用来管理键值对,当内存不足时,软引用的键值对会被回收以释放内存。
3. 弱引用
只要一GC,它就被干掉。 弱引用通常用于那些需要被跟踪但又不希望阻止对象被回收的场景,比如在某些观察者模式的实现中。
ThreadLocal通过每个线程维护一个独立的ThreadLocalMap来实现线程局部变量的存储。
每个ThreadLocalMap中存储的是Entry对象,Entry对象中包含了对ThreadLocal实例的弱引用以及存储的实际值。
使用弱引用的目的是为了在没有外部强引用指向ThreadLocal实例时,能够让垃圾回收器自动回收对应的Entry,避免出现内存泄漏。因为如果使用强引用,即使ThreadLocal对象不再被使用,但由于线程可能一直存在,会导致其关联的对象无法被回收。
WeakHashMap用弱引用来管理键。当某个键不被强引用指向时,这个键值对会自动从WeakHashMap中移除。
在需要实现基于弱键的缓存或者需要自动清理不再被使用的键值对的场景中有用。 例如,需要缓存一些数据,但又不希望这些缓存数据一直占用内存,可以用WeakHashMap。
4. 虚引用
对象是否有虚引用,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。
虚引用主要用来跟踪对象被GC的活动,在对象被回收之前,可以通过ReferenceQueue获取到对应的虚引用,从而进行一些清理操作。
FileChannel的map()方法将文件映射到内存中时,JVM会使用虚引用来跟踪这些内存映射区域。一旦这些映射区域对应的文件不再被使用,并且垃圾回收器准备回收这些内存时,虚引用会被加入到关联的引用队列中。这样可以让程序有机会在文件映射的内存区域被回收之前进行一些清理操作,比如解除文件锁定、关闭文件描述符等。
java中堆外内存的分配方式有3种,上面说的FileChannel的map方法是一种,另外两种分别是:
- ByteBuffer.allocateDirect():分配的堆外内存由JVM机制来释放,不涉及虚引用;
- 使用Unsafe类:分配的堆外内存由开发者自己释放,也不涉及虚引用。