前言
四种引用类型在垃圾收集方面及使用场景上的不同。
强引用
java中默认的引用方式,通过“new”关键字创建的对象,只要存在强引用,垃圾收集器就不会回收对象。
软引用
允许对象在系统内存不足时被回收。当系统内存不足时,垃圾回收器会尝试回收软引用指向的对象,但只有在 JVM 内存不足时才会真正回收。
使用场景:对于需要缓存数据,但又可以在内存不足时释放的情况,如图片缓存、数据缓存等。
1 2 3
| SoftReference<Object> sr = new SoftReference<>(new Object());
sr.get();
|
guava中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Cache<String, String> cache = CacheBuilder.newBuilder() .softValues() .expireAfterWrite(10, TimeUnit.MINUTES) .build();
cache.put("key1", "value1"); cache.put("key2", "value2");
String value1 = cache.getIfPresent("key1"); String value2 = cache.getIfPresent("key2");
System.out.println("Value 1: " + value1); System.out.println("Value 2: " + value2);
cache.invalidateAll();
|
其中softValues方法源码:
1 2 3
| public CacheBuilder<K, V> softValues() { return setValueStrength(Strength.SOFT); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| SOFT { @Override <K, V> ValueReference<K, V> referenceValue( Segment<K, V> segment, ReferenceEntry<K, V> entry, V value, int weight) { return (weight == 1) ? new SoftValueReference<K, V>(segment.valueReferenceQueue, value, entry) : new WeightedSoftValueReference<K, V>( segment.valueReferenceQueue, value, entry, weight); }
@Override Equivalence<Object> defaultEquivalence() { return Equivalence.identity(); } }
|
SOFT枚举实例中就有SoftValueReference相关的源码。
弱引用
即使系统内存充足,只要对象只被弱引用所引用,垃圾回收器就可能会回收它。
使用场景:维护一种非强制性的映射关系,如果获取时对象还在,就使用它,否则需要实例化。对于临时性的对象引用,如事件监听器、缓存中的临时数据等。
1 2 3
| WeakReference<Object> wr = new WeakReference<>(new Object());
wr.get();
|
guava也有相关的配置使用,同上。
ThreadLocal中使用:
1 2 3
| ThreadLocal<String> threadLocal = new ThreadLocal<>(); threadLocal.set("xxx"); threadLocal.get();
|
其中ThreadLocalMap的entry类就是继承自WeakReference类,其中key是弱引用对象。
为什么设计为弱引用,其实是理论上的考虑,threadLocal一般用于private static修饰,threadLocal对象持有的线程比使用threadLocal的线程短时,threadlocal对象指针被回收,如果是强引用,threadlocal对象不会被回收,是弱引用,会回收。
threadlocal对象指针——指向—-> threadlocal对象 <————强引用———- entry<——-强引用—–threadlocalmap<———-thread
threadlocal对象指针没了,threadlocal不会回收。
threadlocal对象指针——指向—-> threadlocal对象 <————弱引用———- entry<——-强引用—–threadlocalmap<———-thread
threadlocal对象指针没了,threadlocal会回收。
maven引入计算对象大小包:
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class ThreadLocalTest { private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException { Thread threada = new Thread(() -> { System.out.println("进入使用threadLocal的线程方法"); String str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; System.out.println(" value is " + RamUsageEstimator.sizeOf(str)); threadLocal.set(str); System.out.println(" value is " + RamUsageEstimator.sizeOf(Thread.currentThread())); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(" value is " + RamUsageEstimator.sizeOf(Thread.currentThread())); System.out.println("threadlocal对象被回收后,entry对象也被回收"); }); threada.start(); Thread.sleep(500); threadLocal = null; System.gc(); System.out.println("回收threadLocal完成"); Thread.sleep(5000); System.out.println("主线程执行完"); } }
|
1 2 3 4 5 6 7
| 进入使用threadLocal的线程方法 value is 648 value is 158864 回收threadLocal完成 value is 158544 threadlocal对象被回收后,entry对象也被回收 主线程执行完
|
虚引用
它在任何时候都可能被垃圾回收器回收,但它不会对对象的生命周期产生影响。虚引用的存在仅仅是为了在对象被回收时收到系统通知。
使用场景:用于跟踪对象被垃圾回收器回收的情况,执行一些清理或日志记录操作。
虚引用必须和引用队列(ReferenceQueue)一起使用。当对象被垃圾回收器回收时,虚引用会被放入引用队列中,以便程序可以在对象被回收时进行一些必要的清理工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue;
public class PhantomReferenceExample { public static void main(String[] args) throws InterruptedException { Object obj = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); PhantomReference<Object> phantomReference = new PhantomReference<>(obj, referenceQueue);
obj = null;
System.gc();
Thread.sleep(1000);
if (referenceQueue.poll() != null) { System.out.println("Phantom reference is enqueued."); } else { System.out.println("Phantom reference is not enqueued."); } } }
|
通常用来做所谓的 Post-Mortem 清理机制,Java 平台自身 Cleaner 机制等,也有人利用幻象引用监控对象的创建和销毁。