碎碎念
JDK1.2之后對對象的引用進行了劃分,分別存在有強引用,軟引用,弱引用,虛引用
- 強引用(Strong Reference)
強引用是最普通的引用,如果一個對象是強引用。那么垃圾回收器絕對不會回收它。當內(nèi)存空間不足時,java虛擬機會拋出OutOfMemoryError是程序異常終止,但是不會回收強引用對象。
- 軟引用(Soft Reference)
一個對象如果是軟引用的話,那么當程序內(nèi)存不足的時候,垃圾回收器會回收它。那內(nèi)存空間不緊張的時候。它就一直存在。也就意味著我們是使用它。
- 弱引用(Weak Reference)
弱引用相比于軟引用它的聲明周期更短。當垃圾回收器在工作的過程中,發(fā)現(xiàn)了弱引用對象,不管當前內(nèi)存是否緊張。都會將其回收。但是GC的觸發(fā)不是特別的頻繁。所以不會那么快發(fā)現(xiàn)這個引用。
- 虛引用 (Phantom Reference)
虛引用顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。
設計一個軟引用緩存集合
由于種種因素,我們需要緩存一些數(shù)據(jù)在內(nèi)存中,假設我們現(xiàn)在需要緩存的數(shù)據(jù)是Person類。( 注意Person對象很大,像BitMap一樣)
public class Person {
private String name;
private byte[] datas = new byte[1024*1024*100];
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
正常情況下,我們是直接使用如下的代碼
public class Main {
public static void main(String[] args) {
Map<Integer, Person> pp = new HashMap<>();
for (int i = 0; i < 100; i++) {
Person person = new Person("hello world");
pp.put(i, person);
}
for (int i = 0; i < 100; i++) {
System.out.println(pp.get(i));
}
}
}
但是一運行,你就會發(fā)現(xiàn)出現(xiàn)如下的錯誤

因為Person的數(shù)據(jù)量太大了,導致JVM出現(xiàn)了OOM。那么有沒有辦法容納下這么多的數(shù)據(jù),而不會出現(xiàn)OOM呢? 嗯~ 沒有辦法。 因為JVM內(nèi)存是有限制的,你沒有辦法無節(jié)制的往內(nèi)存中存放東西。但是我們可以換個角度,來思考。盡可能的利用JVM內(nèi)存呢?嗯~ 可以的,我們可以利用上面所說的軟引用特性來設計一個緩存集合。
/***
* 軟引用應用設計集合
* @param <K> Key
* @param <V> Value
*/
public class SoftReferenceCache<K, V> {
private Map<K, SoftReference<V>> mCache = new HashMap<>();
/**
* 存放數(shù)據(jù)
*
* @param k 數(shù)據(jù)Key
* @param v 數(shù)據(jù)Value
*/
public void put(K k, V v) {
mCache.put(k, new SoftReference<V>(v));
}
/**
* 存放數(shù)據(jù)
*
* @param k 數(shù)據(jù)Key
* @return 返回數(shù)據(jù)Value
*/
public V get(K k) {
SoftReference<V> reference = mCache.get(k);
if (reference != null) {
return reference.get();
}
return null;
}
}
然后使用上面的 SoftReferenceCache 來進行存儲
public static void main(String[] args) {
SoftReferenceCache<Integer, Person> softReferenceCache = new SoftReferenceCache<>();
for (int i = 0; i < 100; i++) {
softReferenceCache.put(i, new Person("hello world" + i));
}
for (int i = 0; i < 100; i++) {
System.out.println(softReferenceCache.get(i));
}
}
運行后,你會發(fā)現(xiàn)不會出現(xiàn)OOM。但是會存在有被GC回收的對象。這個時候,你需要在自己的邏輯上增加三級緩存。

如何知道一個對象被GC回收呢?
我們想要知道一個對象什么時候會被GC回收。就需要利用引用隊列ReferenceQueue,GC在準備回收一個對象時,如果發(fā)現(xiàn)它還僅有軟引用(或弱引用,或虛引用)指向它,就會在回收該對象之前,把這個軟引用(或弱引用,或虛引用)加入到與之關聯(lián)的引用隊列(ReferenceQueue)中,這樣我們就知道,對象被GC回收了。
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Person> queue = new ReferenceQueue<>();
WeakReference<Person> cc = new WeakReference<>(new Person("hello"), queue);
System.gc();
Thread.sleep(2000);
Object object;
while ((object = queue.poll()) != null) {
System.out.println("GC回收了>>>>:" + object);
}
}
上述代碼,可以知道cc什么時候被回收

Android 中的 Reference
- Android 不建議使用
軟引用來做緩存,因為Android Runtime無法感知,應該通過GC來回收內(nèi)存,還是增加APP的棧內(nèi)存。官方推薦我們是LruCache

- 當我們能夠了解一個對象的銷毀時機,就意味著我們能對一些本該銷毀的對象進行反應。 也就是
內(nèi)存泄露的檢測。LeakCanary底層就是使用引用隊列進行對Activity、Fragment進行檢測的。
相關連接
https://developer.android.com/reference/java/lang/ref/package-summary
https://github.com/square/leakcanary