# Java 引用
Java
对象的引用被划分为4种级别,分别为强引用、软引用、弱引用以及虚引用。帮助程序更加灵活地控制对象的生命周期和JVM进行垃圾回收。
# 强引用
强引用是最普遍的引用,一般把一个对象赋给一个引用变量,这个引用变量就是强引用。这个引用保存在Java
栈中,而真正的引用内容保存在Java
堆中。
// 强引用
Object obj=new Object();
2
如果使用了强引用,垃圾回收器GC
时不会回收该对象,当空间不足时,JVM宁愿抛出OutOfMemoryError异常。因为JVM
认为强引用的对象是用户正在使用的对象,它无法分辨出到底该回收哪个,强行回收有可能导致系统严重错误。如果想GC
时回收该对象,让其超出对象的生命周期范围或者设置引用指向null,并且会执行对象的`finalize 函数。如下:帮助垃圾回收期回收此对象,具体什么时候收集这要取决于GC算法。
obj = null;
举个例子: StrongRefenenceDemo
中尽管o1
已经被回收,但是o2
强引用 o1
,一直存在,所以不会被GC
回收。
public class StrongRefenenceDemo {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = o1;
o1 = null;
System.gc(); // 其实就算我们显示调用,GC 也可能不会立即执行
System.out.println(o1); //null
System.out.println(o2); //java.lang.Object@4534fdg7
}
}
2
3
4
5
6
7
8
9
10
11
# 软引用
对象处在有用但非必须的状态,只有当内存空间不足时,GC会回收该引用对象的内存。可以用来实现高速缓存,比如网页缓存、图片缓存等。需要通过SoftReference
类来实现。
// 注意:sr引用也是强引用,它是指向SoftReference这个对象的,
// 这里的软引用指的是指向new String("str")的引用,也就是SoftReference类中T
SoftReference<String> sr = new SoftReference<String>(new String("str"));
2
3
软引用可以和一个引用队列ReferenceQueue
联合使用,如果软引用所引用对象被垃圾回收,Java
虚拟机就会把这个软引用加入到与之关联的引用队列中。
ReferenceQueue<Object> queue = new ReferenceQueue<>();
Object obj = new Object();
SoftReference softRef = new SoftReference<Object>(obj,queue);//删除强引用
obj = null;//调用gc
System.gc();
System.out.println("gc之后的值: " + softRef.get()); // 对象依然存在
//申请较大内存使内存空间使用率达到阈值,强迫gc
byte[] bytes = new byte[100 * 1024 * 1024];//如果obj被回收,则软引用会进入引用队列
Reference<?> reference = queue.remove();
if (reference != null){
System.out.println("对象已被回收: "+ reference.get()); // 对象为null
}
2
3
4
5
6
7
8
9
10
11
12
案例一: SpringBoot
源码中大量使用ConcurrentReferenceHashMap
合理的使用内存,间接的优化JVM
,提高垃圾回收效率。
public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
private static final ConcurrentReferenceHashMap.ReferenceType DEFAULT_REFERENCE_TYPE;
static {
DEFAULT_REFERENCE_TYPE = ConcurrentReferenceHashMap.ReferenceType.SOFT;
}
......
}
2
3
4
5
6
7
SpringFactoriesLoad
源码:Springboot SPI
机制的主角
public final class SpringFactoriesLoader {
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
......
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
......
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
AnnotationsScanner
自动配置过程中的注解扫描
abstract class AnnotationsScanner {
private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationCache = new ConcurrentReferenceHashMap(256);
private static final Map<Class<?>, Method[]> baseTypeMethodsCache = new ConcurrentReferenceHashMap(256);
2
3
ThreadPoolTaskExecutor
异步任务线程池
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
public ThreadPoolTaskExecutor() {
this.decoratedTaskMap = new ConcurrentReferenceHashMap(16, ReferenceType.WEAK);
}
2
3
4
案例二: Mybatis
缓存类SoftCache
用到的软引用:
public Object getObject(Object key) {
Object result = null;
SoftReference<Object> softReference = (SoftReference)this.delegate.getObject(key);
if (softReference != null) {
result = softReference.get();
if (result == null) {
this.delegate.removeObject(key);
} else {
synchronized(this.hardLinksToAvoidGarbageCollection) {
this.hardLinksToAvoidGarbageCollection.addFirst(result);
if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
this.hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}
return result;}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 弱引用
弱引用就是只要JVM垃圾回收器发现了它,不管内存是否足够,都会被回收。
Object object= new Object();
WeakReference<Object> weakReference= new WeakReference<>(object);
Object result = weakReference.get();
2
3
案例: WeakHashMap
当key
只有弱引用时,GC
发现后会自动清理键和值,作为简单的缓存表解决方案。
public class WeakHashMapDemo {
public static void main(String[] args) throws InterruptedException {
myHashMap();
myWeakHashMap();
}
public static void myHashMap() {
HashMap<String, String> map = new HashMap<String, String>();
String key = new String("k1");
String value = "v1";
map.put(key, value);
System.out.println(map);
key = null;
System.gc();
System.out.println(map);
}
public static void myWeakHashMap() throws InterruptedException {
WeakHashMap<String, String> map = new WeakHashMap<String, String>();
String key = new String("weak");
String value = "map";
map.put(key, value);
System.out.println(map);
//去掉强引用
key = null;
System.gc();
Thread.sleep(1000);
System.out.println(map);
}
}
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
ThreadLocal: ThreadLocal.ThreadLocalMap.Entry
继承了弱引用,key
为当前线程实例,和WeakHashMap
基本相同。
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//......}
2
3
4
5
6
7
8
9
10
11
# 虚引用
虚引用和没有引用是一样的,需要和队列ReferenceQueue
联合使用。当jvm扫描到虚引用的对象时,会先将此对象放入关联的队列中,因此我们可以通过判断队列中是否存这个对象,来进行回收前的一些处理。有哨兵的作用。
Object object= new Object();
ReferenceQueue queue = new ReferenceQueue();
PhantomReference pr = new PhantomReference(object, queue);
2
3