示例代碼:
import java.lang.ref.SoftReference;
/**
* @author chenjc
* @since 2020-01-13
*/
public class SoftReferenceTest {
/**
* 使用JVM參數-Xmx10m運行程序
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
User user = new User(1, "debo");
// 建立User對象的軟引用
SoftReference<User> userSoftReference = new SoftReference<>(user);
// 去掉強引用
user = null;
System.out.println(userSoftReference.get());
// 手動觸發GC
System.gc();
System.out.println("第一次GC: " + userSoftReference.get());
// 分配適量內存空間,造成內存資源緊張,產生GC,同時又不會導致堆內存溢出
byte[] bytes = new byte[6 * 1024 * 1050];
System.out.println("第二次GC: " + userSoftReference.get());
}
private static class User {
private Integer id;
private String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
}
使用JVM參數-Xmx10m運行此程序,輸出如下:
User{id=1, name='debo'}
第一次GC: User{id=1, name='debo'}
第二次GC: null
第一次GC的時候,軟引用沒有被回收,是因爲這時候內存資源充足。第二次由於分配了較大的內存,導致GC,這時候由於內存資源緊張,軟引用被回收了,也就是雖然User對象有一個軟引用在引用着它,但User對象在此條件下也會被GC回收。所以軟引用的對象在一定條件下可被回收,故軟引用對象不會導致內存溢出。
軟引用到底有沒有被回收,可以通過給軟引用一個ReferenceQueue來跟蹤,將上面的代碼片段稍作修改,如下:
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
/**
* @author chenjc
* @since 2020-01-13
*/
public class SoftReferenceTest {
/**
* 使用JVM參數-Xmx10m運行程序
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
User user = new User(1, "debo");
// 建立User對象的軟引用
ReferenceQueue<User> userReferenceQueue = new ReferenceQueue<>();
UserSoftReference userSoftReference = new UserSoftReference(user, userReferenceQueue);
// 去掉強引用
user = null;
System.out.println(userSoftReference.get());
// 手動觸發GC
System.gc();
System.out.println("第一次GC: " + userSoftReference.get());
System.out.println("第一次GC隊列: " + userReferenceQueue.remove(1000));
// 分配適量內存空間,造成內存資源緊張,產生GC,同時又不會導致堆內存溢出
byte[] bytes = new byte[6 * 1024 * 1055];
System.out.println("第二次GC: " + userSoftReference.get());
Reference<? extends User> reference = userReferenceQueue.remove(1000);
if (reference != null) {
UserSoftReference userSoftReference1 = (UserSoftReference) reference;
System.out.println("第二次GC隊列: " + userSoftReference1.getId());
}
}
private static class User {
private Integer id;
private String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
private static class UserSoftReference extends SoftReference<User> {
private Integer id;
public UserSoftReference(User referent, ReferenceQueue<? super User> q) {
super(referent, q);
this.id = referent.id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
}
輸出如下:
User{id=1, name='debo'}
第一次GC: User{id=1, name='debo'}
第一次GC隊列: null
第二次GC: null
第二次GC隊列: 1
第一次GC沒有回收軟引用對象,所以ReferenceQueue爲空,第二次GC回收了軟引用對象,所以ReferenceQueue隊列不爲空,那爲什麼可以強轉成UserSoftReference呢?是因爲隊列裏面的reference就是方法局部變量userSoftReference。此處自定義一個UserSoftReference類主要是爲了跟蹤User對象的id,你無法跟蹤User對象,因爲User對象已經被回收了,如果調用reference.get()
,將會返回null。