一、强引用
String s = "kkk";
s属于强引用,只要s属于根可达对象,则就算内存溢出,gc也不会回收
二、软应用
gc正常不会回收,除非内存不够用则会回收,多用于缓存机制场景;可单独开启线程监控引用队列,防止无效的软引用浪费内存;
可通过vm配置演示下面代码示例
-Xms4m -Xmx4m -XX:+PrintGC
public class TestSoftReferance {
private static final List<Object> TEST_DATA = new LinkedList<>();
public static void main(String[] args) throws InterruptedException {
String s = new String("kkk");
ReferenceQueue<String> q = new ReferenceQueue<String>();//指定引用队列
SoftReference<String> reference = new SoftReference<String>(s,q);
s = null;//去掉强引用,使得字符串对象只剩下软引用
System.out.println(reference.get());//输出kkk
// 该线程不断读取这个虚引用,并不断往列表里插入数据,以促使系统早点进行GC
new Thread(() -> {
for (int i=1;;i++) {
System.out.println("===="+i+"===");
TEST_DATA.add(new byte[1024 * 200]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}).start();
// 这个线程不断读取引用队列,当弱引用指向的对象呗回收时,该引用就会被加入到引用队列中
new Thread(() -> {
while (true) {
Reference<? extends String> poll = q.poll();
if (poll != null) {
System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);//打印弱引用对象
System.out.println("--- 回收对象 ---- " + poll.get());//既然都已经被回收了,这里自然get不到了
}
}
}).start();
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
}
}
执行结果:
可以看出是内存快不够用的时候才被回收掉的
三、弱引用
gc回收的时候,如果发现了则会回收,主要在于发现与否。
public class TestWeakReference {
private static final List<Object> TEST_DATA = new LinkedList<>();
public static void main(String[] args) throws InterruptedException {
String s = new String("kkk");
String s1 = new String("kkk");
ReferenceQueue<String> q = new ReferenceQueue<String>();//指定引用队列
WeakReference<String> reference = new WeakReference<String>(s,q);
WeakReference<String> reference2 = new WeakReference<String>(s1,q);
s = null;//去掉强引用,使得字符串对象只剩下弱引用
s1 = null;//去掉强引用,使得字符串对象只剩下弱引用
System.out.println(reference.get());//输出kkk
// 该线程不断读取这个虚引用,并不断往列表里插入数据,以促使系统早点进行GC
new Thread(() -> {
for (int i=1;;i++) {
System.out.println("===="+i+"===");
TEST_DATA.add(new byte[1024 * 200]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}).start();
// 这个线程不断读取引用队列,当弱引用指向的对象呗回收时,该引用就会被加入到引用队列中
new Thread(() -> {
while (true) {
Reference<? extends String> poll = q.poll();
if (poll != null) {
System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);//打印弱引用对象
System.out.println("--- 回收对象 ---- " + poll.get());//既然都已经被回收了,这里自然get不到了,所以打印null
}
}
}).start();
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
}
}
执行结果:
可以看出第3次GC的时候就被发现并回收了
四、虚引用
当试图通过虚引用的get()方法取得强引用时,总是会返回null,并且,虚引用必须和引用队列一起使用。既然这么虚,那么它出现的意义何在??
每次gc肯定都会回收,会被gc当做正常垃圾处理;可以通过引用队列追踪垃圾回收的时间和频率。
public class TestPhantomReference {
private static final List<Object> TEST_DATA = new LinkedList<>();
public static void main(String[] args) throws InterruptedException {
String obj = new String("kkk");//注意:这里如果换成String obj = "kkk";则不会被gc回收,因为该对象存在常量池中;
ReferenceQueue<String> q = new ReferenceQueue<String>();//指定引用队列
PhantomReference<String> reference = new PhantomReference<String>(obj,q);
obj = null;//去掉强引用,使得字符串对象只剩下虚引用
System.out.println(reference.get());//输出null
// 该线程不断读取这个虚引用,并不断往列表里插入数据,以促使系统早点进行GC
new Thread(() -> {
for (int i=1;;i++) {
System.out.println("===="+i+"===");
TEST_DATA.add(new byte[1024 * 200]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}).start();
// 这个线程不断读取引用队列,当弱引用指向的对象呗回收时,该引用就会被加入到引用队列中
new Thread(() -> {
while (true) {
Reference<? extends String> poll = q.poll();
if (poll != null) {
System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
System.out.println("--- 回收对象 ---- " + poll.get());//既然都已经被回收了,这里自然get不到了
}
}
}).start();
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
}
}
运行结果如下:
第1次YGC的时候就给回收掉了
五、引用队列概念:
除强引用外的引用如果失去引用的对象,则引用本身会进入引用队列;