JDK1.2之后,显示支持 强、软、弱、虚 四种类型引用
(1)Strong Reference 强引用,只要强引用还存在:JVM 就不会回收这种对象
所谓强引用就是我们最常使用的,类似于 User u = new User()
那么就说 u 持有 User 对象的强引用:且只要 User 对象还被u持有,那么就不会被回收
(2)Soft Reference 软引用,在系统内存溢出之前,会把软引用持有的对象回收
如果这时内存还是不够,才会发生内存溢出。像很多组件就设计使用软引用来持有缓存对象。
(3)Weak Reference 弱引用,被弱引用关联的对象只能存活到下一次JVM垃圾回收之前
(4)Phantom Reference 虚引用,最弱的一种引用关系,你甚至都不能使用get方法
从虚引用获取其关联的对象
package com.yli.jvm;
/**
* Strong Reference 强引用:由 new Objec() 形式创建的对象
* 如果谁持有该对象的引用,那么就是强引用,比如:
* User u = new User(); 那么就说 u 持有 User 对象的强引用
* 所以很好理解强引用:他就是我们最常使用的引用
*
* 那么强引用对象在什么时候才会被 JVM 回收呢?
* 当没有任何引用到达该对象时,JVM会自行决定的何时回收该对象
*
* 比如上述 User 对象,如果我们设置 u = null;
* 那么在这种最简单情况下:我们说没有任何引用可到达之前创建的 User 对象
* 这时 JVM 会对 User 对象进行第一次标记,并将标记后的对象放入一个低优先级队列
* 再JVM下次GC之前又会进行第二次标记,如果对象没有实现finalize方法
* 那么 JVM 就认定 User 对象已经可以回收了!
*
* 从上面的描述中发现什么特点了么:就是 finalize 方法
* 如果你再这个方法里面 拯救 User 对象,那意味着User可以复活的...
*
*
* 新生代10M,初始堆和最大堆都是20M
* -Xmn10M -Xms20M -Xmx20M
*
* @author yli
*/
public class TestStrongReference {
private static int _8MB = 8 * 1024 * 1024;
private static TestStrongReference test = new TestStrongReference();
public static void main(String[] args) {
// testOOM();
testSaveSelf();
}
/**
* 这个方法用来演示 GC 过程
* 即如果实现了 finalize 方法,在GC过程中会调用一次
* 在这次调用过程中对象有机会复活自己,如果没有复活成功
* 那么就没机会了,因为finalize方法只会执行一次!
*/
private static void testSaveSelf() {
try {
// 设置为 null,再运行 gc 方法
// 可以明显发现 finalize 方法被执行
test = null;
System.gc();
// 延迟500ms 等待 gc 完成
Thread.sleep(500);
// 成功复活自己
if (test == null) {
System.out.println("test 对象已死!");
} else {
System.out.println("test 对象活着!");
}
// 重复来一次上述过程
test = null;
// 这一次 finalize 不会执行了,因为已经执行过一次了!
System.gc();
// 延迟500ms 等待 gc 完成
Thread.sleep(500);
// 复活失败
if (test == null) {
System.out.println("test 对象已死!");
} else {
System.out.println("test 对象活着!");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 简单测试堆内存溢出
* 这里虽然也会执行GC,并且会运行 finalize 方法
* 但明显没有啥内存可回收:因为obj1持有强引用且不释放
* 所以再次创建 obj2 时会内存溢出
*
*/
private static void testOOM() {
// 消耗 8M 内存
byte[] obj1 = new byte[_8MB];
System.out.println("---->内存还不会溢出!");
try {
test = null;
System.gc();
// 延迟500ms 等待 gc
Thread.sleep(500);
byte[] obj2 = new byte[_8MB];
} catch (OutOfMemoryError err) {
System.out.println("---->内存会溢出!");
err.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 如果JVM进行GC操作,肯定会打印如下消息
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("-------->JVM 回收内存!");
// 拯救 test 对象
test = this;
}
}
package com.yli.jvm;
import java.lang.ref.SoftReference;
/**
* 软引用SoftReference:引用关系比强引用低一个等级
* 当系统内存不足可能导致内存溢出之前,JVM会把软引用关联的对象回收
* 如果这时系统内存仍然不足才会发生内存溢出!
*
* 新生代5M,初始堆和最大堆都为10M(即不允许堆内存扩展)
* -Xmn5M -Xms10M -Xmx10M
*
* @author yli
*/
public class TestSoftReference {
private static int _8MB = 8 * 1024 * 1024;
public static void main(String[] args) throws InterruptedException {
// 消耗8MB内存空间
SoftArticle article = new SoftArticle(new byte[_8MB]);
SoftReference softRef = new SoftReference(article);
// article 是 new SoftUser()创建的对象,因此他持有SoftUser的【强引用】
// 此处设置为 article = null非常重要
// 这保证了刚才创建的 SoftUser对象只被 【softRef】持有引用
// 并且是持有:【软引用】
article = null;
// 测试:由 【软引用】 关联的对象是否还活着!
testAliveOrDead(softRef);
// 重新创建一个强引用对象:需再次消耗8MB内存空间
article = new SoftArticle(new byte[_8MB]);
// 再次测试:由 【软引用】 关联的对象是否还活着!
testAliveOrDead(softRef);
}
// 从软引用的get方法就能判断对象是否已经被回收!
private static void testAliveOrDead(SoftReference softRef) {
if (null == softRef.get()) {
System.out.println("额...我已经被JVM回收...已经死亡!");
} else {
System.out.println("额...我还活着!");
}
}
}
class SoftArticle {
private byte[] content;
public SoftArticle(byte[] content) {
this.content = content;
}
public byte[] getContent() {
return this.content;
}
}
package com.yli.jvm;
import java.lang.ref.WeakReference;
/**
* 弱引用:对象只能存活到下一次垃圾回收之前
*
* @author yli
*/
public class TestWeakReference {
public static int count = 0;
private static TestWeakReference tref = new TestWeakReference();
// user 持有 WeakUser 对象的引用
private static WeakUser user = new WeakUser("ali");
public static void main(String[] args) throws InterruptedException {
/**
* 如果一个对象只被WeakReference关联
* 那么该对象只能存活到下一次垃圾回收之前
*/
// user 对象被 weakRef 即一个弱引用关联
WeakReference weakRef = new WeakReference(user);
// 这样保证 只有 weakRef 持有 WeakUser 对象的引用
user = null;
System.out.println("user--->" + weakRef.get());
count++;
tref = null;
System.gc();
Thread.sleep(500);
System.out.println("user--->" + weakRef.get());
count++;
tref = new TestWeakReference();
tref = null;
System.gc();
Thread.sleep(500);
System.out.println("user--->" + weakRef.get());
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println(String.format("第%s次内存回收!", count));
}
}
class WeakUser {
private String name;
public WeakUser(String n) {
this.name = n;
}
public String toString() {
return String.format("{name:%s}", name);
}
}
package com.yli.jvm;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
/**
* 虚引用:最弱的引用关系
* 使用虚引用,你可能只是想在垃圾回收之前收到通知!
*
* JVM 参数设置为查看GC过程详细信息
* -XX:+PrintGCDetails
*
* @author yli
*/
public class TestPhantomReference {
public static void main(String[] args) {
/**
* 这里只演示:一旦使用虚引用关联该对象,就无法从虚引用获取该对象了!
*/
PhantomUser user = new PhantomUser();
ReferenceQueue queue = new ReferenceQueue();
PhantomReference pref = new PhantomReference(user, queue);
// 打印为 null
System.out.println(pref.get());
}
}
class PhantomUser {
}