强引用、软引用、弱引用、虚引用测试
强引用
除非GC Roots不可达,否则宁愿OOM也不回收引用
/**
* 除非GC Roots不可达,否则宁愿OOM也不回收引用
* 启动参数:
* -Xmx16m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:NewSize=2m -XX:MaxNewSize=2m
*/
private static void strongRefenceTest() {
final int _8M = 8 * 1024 * 1024;
List<byte []> list = new ArrayList<byte[]>();
System.out.println("add 8m -1");
list.add(new byte[_8M]);
System.out.println("add 8m -2");
list.add(new byte[_8M]);//OOM异常
list.stream().forEach(r-> System.out.println(r));
}
效果
软引用
内存足够,不会回收软引用对应的内存。内存不足,GC时会回收其内存
1.手动gc
/**
* 软引用手动gc ,由于内存足够,不会回收软引用对应的内存
* @throws InterruptedException
*/
private static void softReferenceGCByManual() throws InterruptedException {
final int _1M = 1 * 1024 * 1024;//直接改为1M测试
List<SoftReference> list = new ArrayList<SoftReference>();
System.out.println("add 1m -1");
list.add(new SoftReference(new byte[_1M]));
System.out.println("add 1m -2");
list.add(new SoftReference(new byte[_1M]));
System.out.println("add 1m -3");
list.add(new SoftReference(new byte[_1M]));
System.gc();//内存足够,不会回收软引用对应的内存
System.out.println("gc---");
TimeUnit.SECONDS.sleep(1);
list.stream().forEach(r-> System.out.println(r.get()));
}
效果:内存足够不会导致回收软引用对应的内存
2.内存不够导致gc
/**
* 仅仅gc, 还不会回收。需内存不够 GC的时候才回收软引用。
* 启动参数:
* -Xmx16m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:NewSize=2m -XX:MaxNewSize=2m
*/
private static void softRefenceTest() {
final int _8M = 8 * 1024 * 1024;
List<SoftReference> list = new ArrayList<SoftReference>();
System.out.println("add 8m -1");
list.add(new SoftReference(new byte[_8M]));
System.out.println("add 8m -2");
list.add(new SoftReference(new byte[_8M]));
System.out.println("add 8m -3");
list.add(new SoftReference(new byte[_8M]));
list.stream().forEach(r-> System.out.println(r.get()));
}
效果:内存不够导致,list中第0,第1个元素被回收。
弱引用
只要gc就会回收内存
/**
* 只要gc就会回收
* 启动参数:
* -Xmx16m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:NewSize=2m -XX:MaxNewSize=2m
*/
private static void WeakReferenceTest() {
Integer a = new Integer(8888);
WeakReference bRef = new WeakReference(a);
a = null;//若不去掉强引用,a对应的Integer对象不能被回收
System.out.println("Run gc");
System.gc();
System.out.println("a:" + bRef.get());
}
效果:只要gc就会回收
虚引用
主要用于代替finalize()
做一些清理操作。
1.PhantomReference#get
永远返回null;
2.被回收的时候,会被投入相关的ReferenceQueue
,可以轮询ReferenceQueue
来处理被gc回收的资源。
展示特性
/**
* -Xmx15m -XX:NewRatio=4 -XX:+PrintGCDetails -XX:+PrintGCDateStamps
* @throws InterruptedException
*/
private static void testPhantomReference() throws InterruptedException {
final int _6M = 6 * 1024 * 1024;//可直接改为1M测试
ReferenceQueue referenceQueue = new ReferenceQueue();
List<Reference> list = new ArrayList<Reference>();
System.out.println("add 6m -1");
PhantomReference aRef = new PhantomReference(new byte[_6M], referenceQueue);//A
System.out.println(aRef.get());//null。避免暴露value造成强引用
list.add(aRef);
System.out.println("add 6m -2");
list.add(new PhantomReference(new byte[_6M], referenceQueue));
System.gc();// 不gc 不会入ReferenceQueue 。若不手动gc,调低数组大小后 ,可能不引起gc导致不入队
list.stream().forEach(r-> System.out.println("refence isEnqueued:"+r.isEnqueued()));//若不gc 则不入队
//入队了两次 。 必须移除队列中对PhantomReference的引用.否则不会回收
if (referenceQueue.remove() != null) {
System.out.println("clean before gc");
}
if (referenceQueue.remove() != null) {
System.out.println("clean before gc");
}
aRef=null;//若不清除该强引用, aRef 所对应的value不会被回收
list.clear();//若不清除对reference的强引用。 list中所有 reference所包含的value不会被回收
//System.gc();//此处可以不手动gc , 已经释放了对PhantomReference的引用-referenceQueue、list、aRef ,再次申请空间的时候会回收不可达的PhantomReference
// 内存不够,引起gc.回收reference所包含的value
System.out.println("add 6m -3");
list.add(new PhantomReference(new byte[_6M], referenceQueue));
System.out.println("add 8m -4");
list.add(new PhantomReference(new byte[_6M], referenceQueue));
}
演示代替finalize()
回收时自动清理
/**
* 演示自动关闭
* https://stackoverflow.com/questions/43311825/how-to-use-phantomreference-as-finalize-replacement
* @Author Holger
* @Date 2019/10/24 0024 下午 4:44
*/
public class TestPhantomReference {
public static void main(String[] args) throws InterruptedException {
// create two Test Objects without closing them
for (int i = 0; i < 2; i++) {
new Test(i);
}
// create two Test Objects with proper resource management
try(Test t2=new Test(2); Test t3=new Test(3)) {
System.out.println("using Test 2 and 3");
}
// Sleep 1 Second, run GC, sleep 1 Second
Thread.sleep(1000);
System.out.println("System.gc()");
System.gc();
Thread.sleep(1000);
}
static class TestResource extends PhantomReference<Test> {
private int id;
private TestResource(int id, Test referent, ReferenceQueue<Test> queue) {
super(referent, queue);
this.id = id;
}
private void close() {
System.out.println("closed "+id);
}
}
public static class Test implements AutoCloseable {
static AutoCloseThread thread = new AutoCloseThread();
static { thread.start(); }
private final TestResource resource;
Test(int id) {
resource = thread.addObject(this, id);
}
public void close() {
resource.close();
thread.remove(resource);
}
}
public static class AutoCloseThread extends Thread {
private ReferenceQueue<Test> mReferenceQueue = new ReferenceQueue<>();
private Set<TestResource> mPhantomStack = new HashSet<>();
public AutoCloseThread() {
setDaemon(true);
}
TestResource addObject(Test pTest, int id) {
final TestResource rs = new TestResource(id, pTest, mReferenceQueue);
mPhantomStack.add(rs);
return rs;
}
void remove(TestResource rs) {
mPhantomStack.remove(rs);
}
@Override
public void run() {
try {
while (true) {
TestResource rs = (TestResource)mReferenceQueue.remove();
System.out.println(rs.id+" not properly closed, doing it now");
mPhantomStack.remove(rs);
rs.close();
}
} catch (InterruptedException e) {
System.out.println("Thread Interrupted");
}
}
}
}
效果:没有使用try-resource的,在被回收的时候被自动关闭了。
演示代替finalize()
回收时自动清理2
public class TestPhantomReference2 {
static class Person extends PhantomReference{
int id;
private Person(Object referent, ReferenceQueue q) {
super(referent, q);
}
public Person(ReferenceQueue q, int id, Object personData) {
super(personData, q);
this.id = id;
}
}
public static void main(String[] args) throws InterruptedException {
final int _6M = 6 * 1024 * 1024;
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> pf = new Person(queue, 0, new byte[_6M]);
new Thread(() -> {
Reference removed;
try {
while ((removed = queue.remove()) != null) {
System.out.println("clean:"+((Person)removed).id);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
int i=1;
while (true) {
Thread.sleep(2000);
pf=new Person(queue, i++, new byte[_6M]);
/*System.gc();
if (pf.get() != null) {
System.out.println("phantomReference Queued: " + pf.isEnqueued()+" => phantomReference : exists");
} else {
System.out.println("phantomReference Queued: " + pf.isEnqueued()+" => phantomReference : null");
}*/
}
}
}
Have you ever used PhantomReference in any project?
Java Reference Objects
java:四种Reference的区别