“为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。”
public static void main(String[] args) throws InterruptedException {
final ReferenceQueue referenceQueue = new ReferenceQueue<>();
new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("GC-Watcher-Thread-%d").daemon(true).build()).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
while (true) {
Reference reference = referenceQueue.poll();
if(reference != null) {
try {
Field referent = Reference.class.getDeclaredField("referent");
referent.setAccessible(true);
System.out.println("gc-->" + referent.get(reference));
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}, 1, 1, TimeUnit.SECONDS);
String testStr = new String("test-str");
PhantomReference prTestStr = new PhantomReference(testStr, referenceQueue);
testStr = null;
Thread.sleep(1000L);
System.gc();
Thread.sleep(2000L);
}
当执行上述代码后:控制台将会打印: gc-->test-str
将上述代码改为如下方式:
static final ReferenceQueue referenceQueue = new ReferenceQueue<>();
public static void main(String[] args) throws InterruptedException {
new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("GC-Watcher-Thread-%d").daemon(true).build()).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
while (true) {
Reference reference = referenceQueue.poll();
if(reference != null) {
try {
Field referent = Reference.class.getDeclaredField("referent");
referent.setAccessible(true);
System.out.println(referent.get(reference));
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}, 1, 1, TimeUnit.SECONDS);
test();
Thread.sleep(1000L);
System.gc();
Thread.sleep(2000L);
}
private static void test() {
String testStr = new String("test-str");
PhantomReference prTestStr = new PhantomReference(testStr, referenceQueue);
}
运行之后没有效果;原因是虚引用prTestStr被系统回收后就不会加到referenceQueue的queue内
将上述代码在做一次修改
static final List> prList = new ArrayList<>();
static final ReferenceQueue referenceQueue = new ReferenceQueue<>();
public static void main(String[] args) throws InterruptedException {
new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("GC-Watcher-Thread-%d").daemon(true).build()).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
while (true) {
Reference reference = referenceQueue.poll();
if(reference != null) {
try {
Field referent = Reference.class.getDeclaredField("referent");
referent.setAccessible(true);
prList.remove(reference);
System.out.println("gc-->" + referent.get(reference));
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}, 1, 1, TimeUnit.SECONDS);
test();
Thread.sleep(1000L);
System.gc();
Thread.sleep(2000L);
}
private static void test() {
String testStr = new String("test-str");
PhantomReference prTestStr = new PhantomReference(testStr, referenceQueue);
prList.add(prTestStr);
}
当执行上述代码后:控制台又打印出了: gc-->test-str
这样我们就可以根据虚引用来跟踪一个对象是否被gc。当我们经常在代码里面使用缓存或者读写文件又或者使用jdbc,不合理的使用例如:使用的数据库连接未关闭
查询结果未关闭、文件流未关闭等等造成了内存泄漏。在我们不确定是否有内存被这些对象泄漏的时候,我们可以通过虚引用的方式进行监控跟踪相关对象在内存的垃圾回收情况