Java中有四种引用,强引用、软引用、弱引用、虚引用。
强引用
强引用不会被回收
1 2
| Object obj = new Object(); String str = new String("str");
|
软引用(SoftReference)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| String str1 = new String("softReferenceTest1"); String str2 = new String("softReferenceTest2"); SoftReference<String> softReference1 = new SoftReference<>(str1); SoftReference<String> softReference2 = new SoftReference<>(str2);
System.out.println(softReference1.get()); System.out.println(softReference2.get());
str2 = null; System.gc(); System.out.println("===gc===");
System.out.println(softReference1.get()); System.out.println(softReference2.get());
|
1 2 3 4 5
| softReferenceTest1 softReferenceTest2 ===gc=== softReferenceTest1 softReferenceTest2
|
可以看到str2并没有被回收。
当内存充足的时候,软引用对象不会被JVM回收,而当内存不足时,软引用对象会被回收掉。
Android的SoftReferences代码注释里写了,要避免使用SoftReferences进行缓存
在实践中,软引用在缓存场景中效率低下。运行时(Runtime)缺乏足够的信息来决定应该清除哪些引用、保留哪些引用。最致命的是,当面临“清除软引用”和“扩展堆内存”之间的选择时,运行时无法做出合理决策。
由于缺乏对每个引用在应用中的价值评估,软引用的实用性大打折扣:
推荐替代方案:LruCache
弱引用(WeakReference)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| String str1 = new String("weakReferenceTest1"); String str2 = new String("weakReferenceTest2"); WeakReference<String> weakReference1 = new WeakReference<>(str1); WeakReference<String> weakReference2 = new WeakReference<>(str2);
System.out.println(weakReference1.get()); System.out.println(weakReference2.get());
str2 = null; System.gc(); System.out.println("===gc===");
System.out.println(weakReference1.get()); System.out.println(weakReference2.get());
|
1 2 3 4 5
| weakReferenceTest1 weakReferenceTest2 ===gc=== weakReferenceTest1 null
|
str1没有被回收,str2被回收了。当没有强引用指向weakReference,一gc就会被回收。Android中经常使用WeakReference来规避内存泄漏的风险。
虚引用(PhantomReference)
1 2 3 4
| ReferenceQueue<String> queue = new ReferenceQueue<>(); String str = new String("phantomReferenceTest"); PhantomReference<String> phantomReference = new PhantomReference<>(str, queue); System.out.println(phantomReference.get());
|
phantomReference.get()永远返回null。
虚引用很少使用,且虚引用只有一个构造方法,必须配合ReferenceQueue一起使用。
配合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 26
| ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
String str = new String("weakReference+referenceQueue"); WeakReference<String> weakReference = new WeakReference<>(str, referenceQueue); System.out.println("weakReference=" + weakReference); System.out.println(weakReference.get()); str = null; System.gc(); System.out.println("===gc===");
try { Thread.sleep(100); } catch (Exception e) { throw new RuntimeException(e); } System.out.println(weakReference.get());
System.gc();
Reference<? extends String> ref = referenceQueue.poll();
if (ref != null) { System.out.println("========"); System.out.println(ref); System.out.println(ref.get()); }
|
1 2 3 4 5 6 7
| weakReference=java.lang.ref.WeakReference@566776ad weakReference+referenceQueue ===gc=== null ======== java.lang.ref.WeakReference@566776ad null
|
可以看到当gc之后weakReference.get()已经为null,但是在referenceQueue.poll()却有值,并且为WeakReference@566776ad,和weakReference相等。也就是 gc之后会将weakReference添加进WeakReference关联的ReferenceQueue。 可以用来监听某个对象是否被回收,android中的LeakCanary
框架就是利用这个原理进行分析是否有内存泄漏。